diff --git a/cmake/lfc.cmake b/cmake/lfc.cmake index 3b736d51d..c812dca6e 100644 --- a/cmake/lfc.cmake +++ b/cmake/lfc.cmake @@ -72,4 +72,4 @@ function(lf_build_generated_code MAIN_TARGET SOURCE_GEN_DIR) target_link_libraries(${MAIN_TARGET} PUBLIC reactor-uc) target_compile_definitions(reactor-uc PUBLIC LF_LOG_LEVEL_ALL=${LOG_LEVEL}) target_compile_definitions(reactor-uc PUBLIC ${LFC_GEN_COMPILE_DEFS}) -endfunction() \ No newline at end of file +endfunction() diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederate.kt b/federated/UcFederate.kt similarity index 82% rename from lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederate.kt rename to federated/UcFederate.kt index 6c182469c..00869438b 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederate.kt +++ b/federated/UcFederate.kt @@ -1,15 +1,16 @@ -package org.lflang.generator.uc +package org.lflang.generator.uc.federated import org.lflang.AttributeUtils import org.lflang.TimeValue import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeTypeFederate +import org.lflang.generator.uc.mics.UcClockSyncParameters +import org.lflang.ir.FederateInstantiation import org.lflang.isBank import org.lflang.lf.Attribute -import org.lflang.lf.Instantiation import org.lflang.target.property.type.PlatformType -class UcFederate(val inst: Instantiation, val bankIdx: Int) { - val isBank = inst.isBank +class UcFederate(val inst: FederateInstantiation, val bankIdx: Int) { + val isBank = inst.codeWidth > 1 val platform: PlatformType.Platform = AttributeUtils.getFederatePlatform(inst) val interfaces = mutableListOf() val codeType = if (isBank) "${inst.codeTypeFederate}_${bankIdx}" else inst.codeTypeFederate @@ -35,8 +36,8 @@ class UcFederate(val inst: Instantiation, val bankIdx: Int) { fun getJoiningPolicy(): JoiningPolicy { val attr: Attribute? = AttributeUtils.getJoiningPolicy(inst) return attr - ?.let { JoiningPolicy.parse(it.getAttrParms().get(0).getValue()) } - .run { JoiningPolicy.JOIN_IMMEDIATELY } + ?.let { JoiningPolicy.Companion.parse(it.getAttrParms().get(0).getValue()) } + .run { JoiningPolicy.IMMEDIATELY } } fun getDefaultInterface(): UcNetworkInterface = interfaces.first() diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt b/federated/UcFederateGenerator.kt similarity index 91% rename from lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt rename to federated/UcFederateGenerator.kt index 6f1d5b704..b1a0ba6c5 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt +++ b/federated/UcFederateGenerator.kt @@ -1,7 +1,14 @@ -package org.lflang.generator.uc +package org.lflang.generator.uc.federated import org.lflang.* import org.lflang.generator.PrependOperator +import org.lflang.generator.uc.UcConnectionGenerator +import org.lflang.generator.uc.UcFileConfig +import org.lflang.generator.uc.UcInstanceGenerator +import org.lflang.generator.uc.UcParameterGenerator +import org.lflang.generator.uc.UcPortGenerator +import org.lflang.generator.uc.UcReactionGenerator +import org.lflang.generator.uc.mics.UcClockSyncGenerator import org.lflang.lf.* import org.lflang.target.TargetConfig diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedLaunchScriptGenerator.kt b/federated/UcFederatedLaunchScriptGenerator.kt similarity index 94% rename from lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedLaunchScriptGenerator.kt rename to federated/UcFederatedLaunchScriptGenerator.kt index 5a4826ee2..ae6f8ee46 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedLaunchScriptGenerator.kt +++ b/federated/UcFederatedLaunchScriptGenerator.kt @@ -1,6 +1,7 @@ -package org.lflang.generator.uc +package org.lflang.generator.uc.federated import org.lflang.generator.PrependOperator +import org.lflang.generator.uc.UcFileConfig import org.lflang.joinWithLn class UcFederatedLaunchScriptGenerator(private val fileConfig: UcFileConfig) { diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedProjectTemplateGenerator.kt b/federated/UcFederatedProjectTemplateGenerator.kt similarity index 99% rename from lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedProjectTemplateGenerator.kt rename to federated/UcFederatedProjectTemplateGenerator.kt index 96fec14d7..344985035 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedProjectTemplateGenerator.kt +++ b/federated/UcFederatedProjectTemplateGenerator.kt @@ -1,4 +1,4 @@ -package org.lflang.generator.uc +package org.lflang.generator.uc.federated import java.nio.file.Files import java.nio.file.Path diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGeneratorFederated.kt b/federated/UcGeneratorFederated.kt similarity index 95% rename from lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGeneratorFederated.kt rename to federated/UcGeneratorFederated.kt index 3948a2b40..046dfdadc 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGeneratorFederated.kt +++ b/federated/UcGeneratorFederated.kt @@ -1,13 +1,16 @@ -package org.lflang.generator.uc +package org.lflang.generator.uc.federated import java.nio.file.Path import java.nio.file.Paths import org.eclipse.emf.ecore.resource.Resource import org.lflang.generator.CodeMap import org.lflang.generator.GeneratorResult -import org.lflang.generator.GeneratorUtils.canGenerate +import org.lflang.generator.GeneratorUtils import org.lflang.generator.LFGeneratorContext +import org.lflang.generator.uc.UcGenerator +import org.lflang.generator.uc.UcGeneratorNonFederated import org.lflang.generator.uc.UcInstanceGenerator.Companion.width +import org.lflang.generator.uc.UcPreambleGenerator import org.lflang.lf.LfFactory import org.lflang.lf.Reactor import org.lflang.reactor @@ -41,7 +44,7 @@ class UcGeneratorFederated(context: LFGeneratorContext, scopeProvider: LFGlobalS srcGenPath: Path, federate: UcFederate ): GeneratorResult.Status { - if (!canGenerate(errorsOccurred(), federate.inst, messageReporter, context)) + if (!GeneratorUtils.canGenerate(errorsOccurred(), federate.inst, messageReporter, context)) return GeneratorResult.Status.FAILED super.copyUserFiles(targetConfig, fileConfig) diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcIpAddress.kt b/federated/UcIpAddress.kt similarity index 94% rename from lfc/core/src/main/kotlin/org/lflang/generator/uc/UcIpAddress.kt rename to federated/UcIpAddress.kt index 3e48713f3..d8db9271a 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcIpAddress.kt +++ b/federated/UcIpAddress.kt @@ -1,6 +1,8 @@ -package org.lflang.generator.uc +package org.lflang.generator.uc.federated import java.math.BigInteger +import java.net.Inet4Address +import java.net.Inet6Address import java.net.InetAddress import java.net.UnknownHostException import java.util.concurrent.atomic.AtomicInteger @@ -23,7 +25,7 @@ sealed class IPAddress { companion object { fun isValidIPv4(ip: String): Boolean { return try { - InetAddress.getByName(ip) is java.net.Inet4Address + InetAddress.getByName(ip) is Inet4Address } catch (e: UnknownHostException) { false } @@ -39,7 +41,7 @@ sealed class IPAddress { companion object { fun isValidIPv6(ip: String): Boolean { return try { - InetAddress.getByName(ip) is java.net.Inet6Address + InetAddress.getByName(ip) is Inet6Address } catch (e: UnknownHostException) { false } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt b/federated/UcNetworkChannel.kt similarity index 98% rename from lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt rename to federated/UcNetworkChannel.kt index ec6db3c73..1ef0616fc 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt +++ b/federated/UcNetworkChannel.kt @@ -1,8 +1,11 @@ -package org.lflang.generator.uc +package org.lflang.generator.uc.federated import org.lflang.AttributeUtils.getInterfaceAttributes import org.lflang.AttributeUtils.getLinkAttribute -import org.lflang.generator.uc.NetworkChannelType.* +import org.lflang.generator.uc.UcFederatedConnectionBundle +import org.lflang.generator.uc.federated.NetworkChannelType.* +import org.lflang.generator.uc.mics.getParamInt +import org.lflang.generator.uc.mics.getParamString import org.lflang.lf.Attribute // An enumeration of the supported NetworkChannels diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorFederated.kt b/federated/UcPlatformGeneratorFederated.kt similarity index 81% rename from lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorFederated.kt rename to federated/UcPlatformGeneratorFederated.kt index b949d3688..0fb702496 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorFederated.kt +++ b/federated/UcPlatformGeneratorFederated.kt @@ -1,6 +1,11 @@ -package org.lflang.generator.uc +package org.lflang.generator.uc.federated import java.nio.file.Path +import org.lflang.generator.uc.UcCmakeGeneratorFederated +import org.lflang.generator.uc.UcMainGeneratorFederated +import org.lflang.generator.uc.UcMakeGeneratorFederated +import org.lflang.generator.uc.UcPlatformGenerator +import org.lflang.ir.Reactor import org.lflang.target.property.NoCompileProperty import org.lflang.target.property.PlatformProperty import org.lflang.target.property.type.PlatformType @@ -9,8 +14,9 @@ import org.lflang.util.FileUtil class UcPlatformGeneratorFederated( generator: UcGeneratorFederated, override val srcGenPath: Path, - private val federate: UcFederate -) : UcPlatformGenerator(generator) { + private val federate: UcFederate, + mainReactor: Reactor +) : UcPlatformGenerator(generator, mainReactor) { override val buildPath = srcGenPath.resolve("build") override val targetName: String = federate.codeType diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStartupCoordinatorGenerator.kt b/federated/UcStartupCoordinatorGenerator.kt similarity index 79% rename from lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStartupCoordinatorGenerator.kt rename to federated/UcStartupCoordinatorGenerator.kt index 1cba85782..19727e6b2 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStartupCoordinatorGenerator.kt +++ b/federated/UcStartupCoordinatorGenerator.kt @@ -1,7 +1,27 @@ -package org.lflang.generator.uc +package org.lflang.generator.uc.federated -import org.lflang.* -import org.lflang.lf.* +import org.lflang.generator.uc.UcConnectionGenerator + +enum class JoiningPolicy { + IMMEDIATELY, + TIMER_ALIGNED; + + companion object { + fun parse(str: String): JoiningPolicy = + when (str) { + "\"IMMEDIATELY\"" -> IMMEDIATELY + "\"TIMER_ALIGNED\"" -> TIMER_ALIGNED + else -> throw IllegalArgumentException("Unknown joining policy: $str") + } + } +} + +fun JoiningPolicy.toCString() = + when (this) { + JoiningPolicy.IMMEDIATELY -> "JOIN_IMMEDIATELY" + JoiningPolicy.TIMER_ALIGNED -> "JOIN_INDIVIDUAL_TIMER_ALIGNED" + else -> throw IllegalArgumentException("Joining policy not handled") + } enum class JoiningPolicy { JOIN_IMMEDIATELY, diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcUARTDevices.kt b/federated/UcUARTDevices.kt similarity index 97% rename from lfc/core/src/main/kotlin/org/lflang/generator/uc/UcUARTDevices.kt rename to federated/UcUARTDevices.kt index 15467b2ff..c674be43d 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcUARTDevices.kt +++ b/federated/UcUARTDevices.kt @@ -1,4 +1,4 @@ -package org.lflang.generator.uc +package org.lflang.generator.uc.federated import java.util.concurrent.atomic.AtomicInteger diff --git a/include/reactor-uc/platform.h b/include/reactor-uc/platform.h index ad87c5892..a86e3568b 100644 --- a/include/reactor-uc/platform.h +++ b/include/reactor-uc/platform.h @@ -23,6 +23,13 @@ struct Mutex { /** Construct a Mutex*/ void Mutex_ctor(Mutex *super); +struct RealtimePlatform { + Platform platform; + + +} + + struct Platform { /** * @brief Return the current physical time in nanoseconds. diff --git a/lfc/bin/lfc-dev b/lfc/bin/lfc-dev index 266424894..0f82a6343 100755 --- a/lfc/bin/lfc-dev +++ b/lfc/bin/lfc-dev @@ -1,4 +1,4 @@ -#!/bin/bash +#!/nix/store/5mh7kaj2fyv8mk4sfq1brwxgc02884wi-bash-5.2p37/bin/bash #============================================================================ # Description: Build and run the Lingua Franca compiler (lfc). diff --git a/lfc/cli/base/src/main/java/org/lflang/cli/CliBase.java b/lfc/cli/base/src/main/java/org/lflang/cli/CliBase.java index a6bc618f6..b8f0f1cff 100644 --- a/lfc/cli/base/src/main/java/org/lflang/cli/CliBase.java +++ b/lfc/cli/base/src/main/java/org/lflang/cli/CliBase.java @@ -298,7 +298,7 @@ public List printErrorsIfAny() { */ public void validateResource(Resource resource) { assert resource != null; - + System.out.println(resource.toString()); List issues = this.validator.validate(resource, CheckMode.ALL, CancelIndicator.NullImpl); for (Issue issue : issues) { diff --git a/lfc/core/src/main/java/org/lflang/AttributeUtils.java b/lfc/core/src/main/java/org/lflang/AttributeUtils.java index 3ea1542de..38fb79b7d 100644 --- a/lfc/core/src/main/java/org/lflang/AttributeUtils.java +++ b/lfc/core/src/main/java/org/lflang/AttributeUtils.java @@ -234,15 +234,6 @@ public static Attribute getJoiningPolicy(Instantiation node) { return findAttributeByName(node, "joining_policy"); } - public static int getMaxNumberOfPendingEvents(Action node) { - Attribute attr = findAttributeByName(node, "max_pending_events"); - if (attr != null) { - return Integer.valueOf(attr.getAttrParms().get(0).getValue()); - } else { - return -1; - } - } - public static int getConnectionBufferSize(Connection node) { Attribute attr = findAttributeByName(node, "buffer"); if (attr != null) { diff --git a/lfc/core/src/main/java/org/lflang/LinguaFranca.xtext b/lfc/core/src/main/java/org/lflang/LinguaFranca.xtext index 3a363e193..0e68b3ff7 100644 --- a/lfc/core/src/main/java/org/lflang/LinguaFranca.xtext +++ b/lfc/core/src/main/java/org/lflang/LinguaFranca.xtext @@ -533,4 +533,4 @@ Token: '@' | // Single quotes "'" -; +; \ No newline at end of file diff --git a/lfc/core/src/main/java/org/lflang/generator/LFGenerator.java b/lfc/core/src/main/java/org/lflang/generator/LFGenerator.java index 47cf0485a..17f7f5c4e 100644 --- a/lfc/core/src/main/java/org/lflang/generator/LFGenerator.java +++ b/lfc/core/src/main/java/org/lflang/generator/LFGenerator.java @@ -1,7 +1,8 @@ package org.lflang.generator; -import static org.lflang.generator.uc.UcGeneratorKt.createUcGenerator; +import static org.lflang.generator.uc2.UcGeneratorKt.createUcGenerator; +import com.google.common.collect.Lists; import com.google.inject.Inject; import com.google.inject.Injector; import java.nio.file.Path; @@ -12,7 +13,12 @@ import org.lflang.FileConfig; import org.lflang.MessageReporter; import org.lflang.ast.ASTUtils; -import org.lflang.generator.uc.UcFileConfig; +import org.lflang.generator.uc2.UcFileConfig; +import org.lflang.generator.uc2.UcGenerator; +import org.lflang.generator.uc2.UcGeneratorNonFederated; +import org.lflang.ir.Environment; +import org.lflang.ir.Federate; +import org.lflang.ir.XTextConverter; import org.lflang.scoping.LFGlobalScopeProvider; import org.lflang.target.Target; @@ -41,29 +47,27 @@ public static FileConfig createFileConfig( // return new FederationFileConfig(resource, srcGenBasePath, useHierarchicalBin); // } - return switch (target) { - // case CCPP, C -> new CFileConfig(resource, srcGenBasePath, useHierarchicalBin); - // case Python -> new PyFileConfig(resource, srcGenBasePath, useHierarchicalBin); - // case CPP -> new CppFileConfig(resource, srcGenBasePath, useHierarchicalBin); - // case Rust -> new RustFileConfig(resource, srcGenBasePath, useHierarchicalBin); - // case TS -> new TSFileConfig(resource, srcGenBasePath, useHierarchicalBin); - case UC -> new UcFileConfig(resource, srcGenBasePath, useHierarchicalBin, runtimeSymlink); - }; + return new UcFileConfig(resource, srcGenBasePath, useHierarchicalBin, runtimeSymlink); } /** Create a generator object for the given target. */ - private GeneratorBase createGenerator(LFGeneratorContext context) { + private UcGenerator createGenerator(Resource resource, LFGeneratorContext context, MessageReporter messageReporter) { final Target target = Target.fromDecl(ASTUtils.targetDecl(context.getFileConfig().resource)); - assert target != null; - return switch (target) { - // case C -> new CGenerator(context, false); - // case CCPP -> new CGenerator(context, true); - // case Python -> new PythonGenerator(context); - // case CPP -> new CppGenerator(context, scopeProvider); - // case TS -> new TSGenerator(context); - // case Rust -> new RustGenerator(context, scopeProvider); - case UC -> createUcGenerator(context, scopeProvider); - }; + var converter = new XTextConverter(resource); + var env = converter.convert(); + + var generator = createUcGenerator( + context, + scopeProvider, + env.getFirst(), + env.getFirst().getMainReactor(), + messageReporter + ); + + var gen1 = (UcGeneratorNonFederated) generator; + gen1.file = env.getSecond(); + + return generator; } @Override @@ -76,18 +80,21 @@ public void doGenerate(Resource resource, IFileSystemAccess2 fsa, IGeneratorCont lfContext = LFGeneratorContext.lfGeneratorContextOf(resource, fsa, context); } + // The fastest way to generate code is to not generate any code. if (lfContext.getMode() == LFGeneratorContext.Mode.LSP_FAST) return; - final GeneratorBase generator = createGenerator(lfContext); - if (generator != null) { - generatorErrorsOccurred = generator.errorsOccurred(); - generator.doGenerate(resource, lfContext); - } final MessageReporter messageReporter = lfContext.getErrorReporter(); if (messageReporter instanceof LanguageServerMessageReporter) { ((LanguageServerMessageReporter) messageReporter).publishDiagnostics(); } + + var generator = createGenerator(resource, lfContext, messageReporter); + var generator1 = (UcGeneratorNonFederated)generator; + generatorErrorsOccurred = generator.errorsOccurred(); + + generator.doGenerate(resource, lfContext); + } /** Return true if errors occurred in the last call to doGenerate(). */ 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 deleted file mode 100644 index b0b7431d2..000000000 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcActionGenerator.kt +++ /dev/null @@ -1,107 +0,0 @@ -package org.lflang.generator.uc - -import org.lflang.* -import org.lflang.AttributeUtils.getMaxNumberOfPendingEvents -import org.lflang.generator.PrependOperator -import org.lflang.generator.orZero -import org.lflang.generator.uc.UcPortGenerator.Companion.arrayLength -import org.lflang.generator.uc.UcPortGenerator.Companion.isArray -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.generator.uc.UcReactorGenerator.Companion.hasShutdown -import org.lflang.generator.uc.UcReactorGenerator.Companion.hasStartup -import org.lflang.lf.* - -class UcActionGenerator(private val reactor: Reactor) { - - companion object { - public val Action.maxNumPendingEvents - get(): Int { - val num = getMaxNumberOfPendingEvents(this) - return if (num > 0) num else 1 - } - } - - /** Returns the C Enum representing the type of action. */ - private val Action.actionType - get(): String = if (isPhysical) "PhysicalAction" else "LogicalAction" - - private fun generateSelfStruct(action: Action): String { - if (action.type == null) { - return "LF_DEFINE_ACTION_STRUCT_VOID(${reactor.codeType}, ${action.name}, ${action.actionType}, ${reactor.getEffects(action).size}, ${reactor.getSources(action).size}, ${reactor.getObservers(action).size}, ${action.maxNumPendingEvents});" - } else if (action.type.isArray) { - return "LF_DEFINE_ACTION_STRUCT_ARRAY(${reactor.codeType}, ${action.name}, ${action.actionType}, ${reactor.getEffects(action).size}, ${reactor.getSources(action).size}, ${reactor.getObservers(action).size}, ${action.maxNumPendingEvents}, ${action.type.id}, ${action.type.arrayLength});" - } else { - return "LF_DEFINE_ACTION_STRUCT(${reactor.codeType}, ${action.name}, ${action.actionType}, ${reactor.getEffects(action).size}, ${reactor.getSources(action).size}, ${reactor.getObservers(action).size}, ${action.maxNumPendingEvents}, ${action.type.toText()});" - } - } - - private fun generateCtor(action: Action) = - with(PrependOperator) { - """ - |LF_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.maxNumPendingEvents} ${if (action.type != null) ", ${action.type.toText()}" else ""}); - | - """ - .trimMargin() - } - - private fun generateCtor(builtin: BuiltinTrigger) = - (if (builtin == BuiltinTrigger.STARTUP) "LF_DEFINE_STARTUP_CTOR" - else "LF_DEFINE_SHUTDOWN_CTOR") + "(${reactor.codeType});\n" - - fun generateCtors(): String { - var code = reactor.allActions.joinToString(separator = "\n") { generateCtor(it) } - if (reactor.hasStartup) code += generateCtor(BuiltinTrigger.STARTUP) - if (reactor.hasShutdown) code += generateCtor(BuiltinTrigger.SHUTDOWN) - return code - } - - private fun generateSelfStruct(builtin: BuiltinTrigger) = - (if (builtin == BuiltinTrigger.STARTUP) "LF_DEFINE_STARTUP_STRUCT" - else "LF_DEFINE_SHUTDOWN_STRUCT") + - "(${reactor.codeType}, ${reactor.getEffects(builtin).size}, ${reactor.getObservers(builtin).size});\n" - - fun generateSelfStructs(): String { - var code = reactor.allActions.joinToString(separator = "\n") { generateSelfStruct(it) } - if (reactor.hasStartup) { - code += generateSelfStruct(BuiltinTrigger.STARTUP) - } - if (reactor.hasShutdown) { - code += generateSelfStruct(BuiltinTrigger.SHUTDOWN) - } - return code - } - - fun generateReactorStructFields(): String { - var code = - reactor.allActions.joinToString( - prefix = "// Actions and builtin triggers\n", separator = "\n", postfix = "\n") { - "LF_ACTION_INSTANCE(${reactor.codeType}, ${it.name});" - } - if (reactor.hasStartup) code += "LF_STARTUP_INSTANCE(${reactor.codeType});" - if (reactor.hasShutdown) code += "LF_SHUTDOWN_INSTANCE(${reactor.codeType});" - return code - } - - private fun generateReactorCtorCode(action: Action) = - "LF_INITIALIZE_ACTION(${reactor.codeType}, ${action.name}, ${action.minDelay.orZero().toCCode()}, ${action.minSpacing.orZero().toCCode()});" - - private fun generateReactorCtorCodeStartup() = "LF_INITIALIZE_STARTUP(${reactor.codeType});" - - private fun generateReactorCtorCodeShutdown() = "LF_INITIALIZE_SHUTDOWN(${reactor.codeType});" - - fun generateReactorCtorCodes(): String { - var code = - reactor.allActions.joinToString( - prefix = "// Initialize actions and builtin triggers\n", - separator = "\n", - postfix = "\n") { - generateReactorCtorCode(it) - } - if (reactor.hasStartup) code += "${generateReactorCtorCodeStartup()}\n" - if (reactor.hasShutdown) code += "${generateReactorCtorCodeShutdown()}\n" - return code - } -} diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcClockSyncGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcClockSyncGenerator.kt deleted file mode 100644 index 96f06b0b9..000000000 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcClockSyncGenerator.kt +++ /dev/null @@ -1,86 +0,0 @@ -package org.lflang.generator.uc - -import org.lflang.* -import org.lflang.lf.* -import org.lflang.target.TargetConfig -import org.lflang.target.property.ClockSyncModeProperty -import org.lflang.target.property.type.ClockSyncModeType - -data class UcClockSyncParameters( - val disabled: Boolean = UcClockSyncParameters.DEFAULT_DISABLED, - var grandmaster: Boolean = UcClockSyncParameters.DEFAULT_GRANDMASTER, - val period: Long = UcClockSyncParameters.DEFAULT_PERIOD, - val maxAdj: Int = UcClockSyncParameters.DEFAULT_MAX_ADJ, - val Kp: Double = UcClockSyncParameters.DEFAULT_KP, - val Ki: Double = UcClockSyncParameters.DEFAULT_KI, -) { - companion object { - const val DEFAULT_DISABLED = false - const val DEFAULT_GRANDMASTER = false - const val DEFAULT_PERIOD = 1000000000L - const val DEFAULT_MAX_ADJ = 200000000 - const val DEFAULT_KP = 0.7 - const val DEFAULT_KI = 0.3 - } - - // Secondary constructor that initializes the data class from an Attribute object - constructor( - attr: Attribute - ) : this( - disabled = attr.getParamBool("disabled") ?: DEFAULT_DISABLED, - grandmaster = attr.getParamBool("grandmaster") ?: DEFAULT_GRANDMASTER, - period = attr.getParamBigInt("period") ?: DEFAULT_PERIOD, - maxAdj = attr.getParamInt("max_adj") ?: DEFAULT_MAX_ADJ, - Kp = attr.getParamFloat("kp") ?: DEFAULT_KP, - Ki = attr.getParamFloat("ki") ?: DEFAULT_KI) -} - -class UcClockSyncGenerator( - private val federate: UcFederate, - private val connectionGenerator: UcConnectionGenerator, - private val targetConfig: TargetConfig -) { - - companion object { - // The number of system events allocated for each neigbor. Used to schedule received messages as - // system events. - val numSystemEventsPerBundle = 3 - - // The number of additional system events allocated. This system event is used for the periodic - // SyncRequest event. The value must match the NUM_RESERVED_EVENTS compile def in - // clock_synchronization. - val numSystemEventsConst = 2 - - // Returns the number of system events needed by the clock sync subsystem, given a number of - // neighbors. - fun getNumSystemEvents(numBundles: Int) = - numSystemEventsPerBundle * numBundles + numSystemEventsConst - - val instName = "clock_sync" - } - - private val numNeighbors = connectionGenerator.getNumFederatedConnectionBundles() - private val numSystemEvents = getNumSystemEvents(numNeighbors) - private val typeName = "Federate" - private val clockSync = federate.clockSyncParams - private val disabled = federate.clockSyncParams.disabled - - fun enabled() = - !disabled && - targetConfig.getOrDefault(ClockSyncModeProperty.INSTANCE) != - ClockSyncModeType.ClockSyncMode.OFF - - fun generateSelfStruct() = - if (enabled()) "LF_DEFINE_CLOCK_SYNC_STRUCT(${typeName}, ${numNeighbors}, ${numSystemEvents})" - else "" - - fun generateCtor() = - if (enabled()) - "LF_DEFINE_CLOCK_SYNC_CTOR(Federate, ${numNeighbors}, ${numSystemEvents }, ${clockSync.grandmaster}, ${clockSync.period}, ${clockSync.maxAdj}, ${clockSync.Kp}, ${clockSync.Ki});" - else "" - - fun generateFederateStructField() = - if (enabled()) "${typeName}ClockSynchronization ${instName};" else "" - - fun generateFederateCtorCode() = if (enabled()) "LF_INITIALIZE_CLOCK_SYNC(${typeName});" else "" -} 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 deleted file mode 100644 index ddd2d19d8..000000000 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt +++ /dev/null @@ -1,94 +0,0 @@ -package org.lflang.generator.uc - -import java.nio.file.Path -import kotlin.io.path.name -import org.lflang.* -import org.lflang.generator.PrependOperator -import org.lflang.lf.Instantiation -import org.lflang.target.TargetConfig -import org.lflang.target.property.* - -abstract class UcCmakeGenerator( - private val targetConfig: TargetConfig, - private val fileConfig: UcFileConfig, -) { - protected val S = '$' // a little trick to escape the dollar sign with $S - private val minCmakeVersion = "3.10" - protected val includeFiles = - targetConfig.get(CmakeIncludeProperty.INSTANCE)?.map { - fileConfig.srcPath.resolve(it).toUnixString() - } - protected val platform = targetConfig.get(PlatformProperty.INSTANCE).platform - abstract val mainTarget: String - - abstract fun generateIncludeCmake(sources: List): String - - fun doGenerateIncludeCmake(sources: List, compileDefs: List) = - with(PrependOperator) { - """ |# This file was generated by the Lingua Franca Compiler for the program $mainTarget - |# It should be included by a CMakeLists.txt to build the final binary. - | - |set(LFC_GEN_SOURCES - ${" | "..sources.filterNot{it.name == "lf_main.c"}.joinWithLn { "$S{CMAKE_CURRENT_LIST_DIR}/${it.toUnixString()}"}} - |) - |set(LFC_GEN_COMPILE_DEFS - ${" | "..compileDefs.joinWithLn { it }} - |) - |set(LFC_GEN_MAIN "$S{CMAKE_CURRENT_LIST_DIR}/lf_main.c") - |set(RUNTIME_PATH $S{CMAKE_CURRENT_LIST_DIR}/reactor-uc) - |set(LFC_GEN_INCLUDE_DIRS $S{CMAKE_CURRENT_LIST_DIR}) - """ - .trimMargin() - } - - fun generateMainCmakeNative() = - with(PrependOperator) { - """ - |# This file was generated by the Lingua Franca Compiler for the program $mainTarget. - |# It is a standalone CMakeLists.txt intended for compiling the program for native execution. - | - |cmake_minimum_required(VERSION $minCmakeVersion) - |project(${mainTarget} LANGUAGES C) - |set(LF_MAIN_TARGET ${mainTarget}) - |set(SOURCE_FOLDER ${fileConfig.srcPath}) - |set(CMAKE_BUILD_TYPE ${targetConfig.getOrDefault(BuildTypeProperty.INSTANCE)}) - |set(PLATFORM POSIX CACHE STRING "Target platform") - |include($S{CMAKE_CURRENT_SOURCE_DIR}/Include.cmake) - |add_executable($S{LF_MAIN_TARGET} $S{LFC_GEN_SOURCES} $S{LFC_GEN_MAIN}) - |install(TARGETS $S{LF_MAIN_TARGET} - | RUNTIME DESTINATION $S{CMAKE_INSTALL_BINDIR} - | OPTIONAL - |) - |add_compile_definitions("LF_LOG_LEVEL_ALL=LF_LOG_LEVEL_${targetConfig.getOrDefault(LoggingProperty.INSTANCE).name.uppercase()}") - |add_compile_definitions($S{LFC_GEN_COMPILE_DEFS}) - |add_subdirectory($S{RUNTIME_PATH}) - |target_link_libraries($S{LF_MAIN_TARGET} PRIVATE reactor-uc) - |target_include_directories($S{LF_MAIN_TARGET} PRIVATE $S{SOURCE_FOLDER}) - |target_include_directories($S{LF_MAIN_TARGET} PRIVATE $S{LFC_GEN_INCLUDE_DIRS}) - ${" |"..(includeFiles?.joinWithLn { "include(\"$it\")" } ?: "")} - """ - .trimMargin() - } -} - -class UcCmakeGeneratorNonFederated( - private val mainDef: Instantiation, - targetConfig: TargetConfig, - fileConfig: UcFileConfig, -) : UcCmakeGenerator(targetConfig, fileConfig) { - override val mainTarget = fileConfig.name - - override fun generateIncludeCmake(sources: List) = - doGenerateIncludeCmake(sources, emptyList()) -} - -class UcCmakeGeneratorFederated( - private val federate: UcFederate, - private val targetConfig: TargetConfig, - fileConfig: UcFileConfig, -) : UcCmakeGenerator(targetConfig, fileConfig) { - override val mainTarget = federate.codeType - - override fun generateIncludeCmake(sources: List) = - doGenerateIncludeCmake(sources, federate.getCompileDefs()) -} 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 deleted file mode 100644 index ebbea8450..000000000 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt +++ /dev/null @@ -1,603 +0,0 @@ -package org.lflang.generator.uc - -import java.util.* -import kotlin.collections.HashSet -import org.lflang.* -import org.lflang.generator.PrependOperator -import org.lflang.generator.orNever -import org.lflang.generator.uc.UcInstanceGenerator.Companion.isAFederate -import org.lflang.generator.uc.UcPortGenerator.Companion.arrayLength -import org.lflang.generator.uc.UcPortGenerator.Companion.isArray -import org.lflang.generator.uc.UcReactorGenerator.Companion.codeType -import org.lflang.lf.* - -/** - * This generator creates code for configuring the connections between reactors. This is perhaps the - * most complicated part of the code-generator. This generator handles both federated and - * non-federated programs - */ -class UcConnectionGenerator( - private val reactor: Reactor, // The reactor to generator connections for - private val currentFederate: - UcFederate?, // The federate to generate connections for. If set then `reactor` should be - // the top-level reactor. - private val allFederates: - List // A list of all the federates in the program. Only used for federated - // code-gen. -) { - - /** A list containing all non-federated gruoped connections within this reactor. */ - private val nonFederatedConnections: List - - /** - * A list containing all federated connection bundles (each containing grouped connections) within - * this reactor, that has the current federate as a src or dest. - */ - private val federatedConnectionBundles: List - - private val isFederated = currentFederate != null - - /** - * Given a LF connection and possibly the list of federates of the program. Create all the - * ConnectionChannels found within the LF Connection. This must handle multiports, banks, iterated - * connections and federated connections. - */ - private fun parseConnectionChannels( - conn: Connection, - federates: List - ): List { - val res = mutableListOf() - val rhsPorts = conn.rightPorts.map { getChannelQueue(it, federates) } - var rhsPortIndex = 0 - - var lhsPorts = conn.leftPorts.map { getChannelQueue(it, federates) } - var lhsPortIndex = 0 - - // Keep parsing out connections until we are out of right-hand-side (rhs) ports - while (rhsPortIndex < rhsPorts.size) { - // First get the current lhs and rhs port and UcGroupedConnection that we are working with - val lhsPort = lhsPorts[lhsPortIndex] - val rhsPort = rhsPorts[rhsPortIndex] - if (rhsPort.channelsLeft() > lhsPort.channelsLeft()) { - val rhsChannelsToAdd = rhsPort.takeChannels(lhsPort.channelsLeft()) - val lhsChannelsToAdd = lhsPort.takeRemainingChannels() - lhsChannelsToAdd.zip(rhsChannelsToAdd).forEach { - res.add(UcConnectionChannel(it.first, it.second, conn)) - } - lhsPortIndex += 1 - } else if (rhsPort.channelsLeft() < lhsPort.channelsLeft()) { - val numRhsChannelsToAdd = rhsPort.channelsLeft() - val rhsChannelsToAdd = rhsPort.takeRemainingChannels() - val lhsChannelsToAdd = lhsPort.takeChannels(numRhsChannelsToAdd) - lhsChannelsToAdd.zip(rhsChannelsToAdd).forEach { - res.add(UcConnectionChannel(it.first, it.second, conn)) - } - rhsPortIndex += 1 - } else { - lhsPort.takeRemainingChannels().zip(rhsPort.takeRemainingChannels()).forEach { - res.add(UcConnectionChannel(it.first, it.second, conn)) - } - rhsPortIndex += 1 - lhsPortIndex += 1 - } - - // If we are out of lhs variables, but not rhs, then there should be an iterated connection. - // We handle it by resetting the lhsChannels variable and index and continuing until - // we have been through all rhs channels. - if (lhsPortIndex >= lhsPorts.size && rhsPortIndex < rhsPorts.size) { - assert(conn.isIterated) - lhsPorts = conn.leftPorts.map { getChannelQueue(it, federates) } - lhsPortIndex = 0 - } - } - return res - } - - /** - * Given a list of ConnectionChannels, group them together. How they are grouepd depends on - * whether we are dealing with federated or non-federated reactors. - */ - private fun groupConnections(channels: List): List { - val res = mutableListOf() - val channels = HashSet(channels) - - while (channels.isNotEmpty()) { - val c = channels.first()!! - - if (c.isFederated) { - val grouped = - channels.filter { - it.conn.delayString == c.conn.delayString && - it.conn.isPhysical == c.conn.isPhysical && - it.src.varRef == c.src.varRef && - it.src.federate == c.src.federate && - it.dest.federate == c.dest.federate && - it.getChannelType() == c.getChannelType() - } - - val srcFed = allFederates.find { it == UcFederate(c.src.varRef.container, c.src.bankIdx) }!! - val destFed = - allFederates.find { it == UcFederate(c.dest.varRef.container, c.dest.bankIdx) }!! - val groupedConnection = - UcFederatedGroupedConnection( - c.src.varRef, - grouped, - c.conn, - srcFed, - destFed, - ) - - res.add(groupedConnection) - channels.removeAll(grouped.toSet()) - } else { - val grouped = - channels.filter { - it.conn.delayString == c.conn.delayString && - it.conn.isPhysical == c.conn.isPhysical && - !it.isFederated && - it.src.varRef == c.src.varRef && - it.src.federate == c.src.federate - } - - val groupedConnection = UcGroupedConnection(c.src.varRef, grouped, c.conn) - res.add(groupedConnection) - channels.removeAll(grouped.toSet()) - } - } - return res - } - - /** - * Given a port VarRef, and the list of federates. Create a channel queue. I.e. create all the - * UcChannels and associate them with the correct federates. - */ - private fun getChannelQueue(portVarRef: VarRef, federates: List): UcChannelQueue { - return if (portVarRef.container?.isAFederate ?: false) { - val federates = allFederates.filter { it.inst == portVarRef.container } - UcChannelQueue(portVarRef, federates) - } else { - UcChannelQueue(portVarRef, emptyList()) - } - } - - companion object { - private val Connection.delayString - get(): String = this.delay.orNever().toCCode() - - /** - * Whether we have initialized the UcFederates with NetworkInterfaces. This is only done once. - */ - private var federateInterfacesInitialized = false - /** - * A global list of FederatedConnectionBundles. It is computed once and reused when - * code-generating - */ - private var allFederatedConnectionBundles: List = emptyList() - - /** - * This function takes a list of grouped connections and creates the necessary - * FederatedConnectionBundles. The bundles are written to the global variable - * allFederatedConnectionBundles and shared accross federates. Thus, this function should only - * be called once during code-gen. - */ - private fun createFederatedConnectionBundles(groupedConnections: List) { - val groupedSet = HashSet(groupedConnections) - val bundles = mutableListOf() - - // This while loop bundles GroupedConnections togheter until there are no more left. - while (groupedSet.isNotEmpty()) { - // Get an unbundled GroupedConnection - val g = groupedSet.first()!! - val toRemove = mutableListOf(g) - if (g is UcFederatedGroupedConnection) { - // Find all other unbundled FederatedGroupedConnection which go between the same two - // federates - val group = - groupedSet.filterIsInstance().filter { - (it.srcFed == g.srcFed && it.destFed == g.destFed) || - (it.srcFed == g.destFed && it.destFed == g.srcFed) - } - - // Create a new ConnectionBundle with these groups - bundles.add(UcFederatedConnectionBundle(g.srcFed, g.destFed, group)) - - // Remove all those grouped connections. - toRemove.addAll(group) - } - groupedSet.removeAll(toRemove.toSet()) - } - allFederatedConnectionBundles = bundles - } - } - - init { - // Only pass through all federates and add NetworkInterface objects to them once. - if (isFederated && !federateInterfacesInitialized) { - for (fed in allFederates) { - UcNetworkInterfaceFactory.createInterfaces(fed).forEach { fed.addInterface(it) } - } - federateInterfacesInitialized = true - } - - // Parse out all GroupedConnections. Note that this is repeated for each federate. - val channels = mutableListOf() - reactor.allConnections.forEach { channels.addAll(parseConnectionChannels(it, allFederates)) } - val grouped = groupConnections(channels) - nonFederatedConnections = mutableListOf() - federatedConnectionBundles = mutableListOf() - - if (isFederated) { - // Only parse out federated connection bundles once for the very first federate - if (allFederatedConnectionBundles.isEmpty()) { - createFederatedConnectionBundles(grouped) - } - - // Filter out the relevant bundles for this federate - federatedConnectionBundles.addAll( - allFederatedConnectionBundles.filter { - it.src == currentFederate || it.dest == currentFederate - }) - // Add all non-federated connections (e.g. a loopback connection) - nonFederatedConnections.addAll( - grouped - .filterNot { it is UcFederatedGroupedConnection } - .filter { - it.channels.fold(true) { acc, c -> acc && (c.src.federate == currentFederate) } - }) - } else { - // In the non-federated case, all grouped connections are handled togehter. - nonFederatedConnections.addAll(grouped) - } - - // Assign a unique ID to each connection to avoid possible naming conflicts in the generated - // code. - val allGroupedConnections = - federatedConnectionBundles - .map { it.groupedConnections } - .flatten() - .plus(nonFederatedConnections) - allGroupedConnections.forEachIndexed { idx, el -> el.assignUid(idx) } - } - - fun getNumFederatedConnectionBundles() = federatedConnectionBundles.size - - fun getNumConnectionsFromPort(instantiation: Instantiation?, port: Port): Int { - var count = 0 - // Find all outgoing non-federated grouped connections from this port - for (groupedConn in nonFederatedConnections) { - if (groupedConn.srcInst == instantiation && groupedConn.srcPort == port) { - count += 1 - } - } - - // Find all outgoing federated grouped connections from this port. - for (federatedConnectionBundle in federatedConnectionBundles) { - for (groupedConn in federatedConnectionBundle.groupedConnections) { - if (groupedConn.srcFed == currentFederate && - groupedConn.srcInst == instantiation && - groupedConn.srcPort == port) { - count += 1 - } - } - } - return count - } - - private fun generateLogicalSelfStruct(conn: UcGroupedConnection) = - "LF_DEFINE_LOGICAL_CONNECTION_STRUCT(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.numDownstreams()});" - - private fun generateLogicalCtor(conn: UcGroupedConnection) = - "LF_DEFINE_LOGICAL_CONNECTION_CTOR(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.numDownstreams()});" - - private fun generateDelayedSelfStruct(conn: UcGroupedConnection) = - if (conn.srcPort.type.isArray) - "LF_DEFINE_DELAYED_CONNECTION_STRUCT_ARRAY(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.numDownstreams()}, ${conn.srcPort.type.id}, ${conn.maxNumPendingEvents}, ${conn.srcPort.type.arrayLength});" - else - "LF_DEFINE_DELAYED_CONNECTION_STRUCT(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.numDownstreams()}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents});" - - private fun generateFederatedInputSelfStruct(conn: UcFederatedGroupedConnection) = - if (conn.srcPort.type.isArray) - "LF_DEFINE_FEDERATED_INPUT_CONNECTION_STRUCT_ARRAY(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.srcPort.type.id}, ${conn.maxNumPendingEvents}, ${conn.srcPort.type.arrayLength});" - else - "LF_DEFINE_FEDERATED_INPUT_CONNECTION_STRUCT(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents});" - - /** - * Get the maximum wait time for a federated input connection. This is the maximum of the - * destination federate's maxwait time and the connection's maxwait time. These times are - * specified using the @maxwait attribute. - */ - private fun getMaxWait(conn: UcFederatedGroupedConnection) = - conn.destFed.getMaxWait().add(conn.getMaxWait()) - - private fun generateFederatedInputCtor(conn: UcFederatedGroupedConnection) = - "LF_DEFINE_FEDERATED_INPUT_CONNECTION_CTOR(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents}, ${conn.delay}, ${conn.isPhysical}, ${getMaxWait(conn).toCCode()});" - - private fun generateDelayedCtor(conn: UcGroupedConnection) = - "LF_DEFINE_DELAYED_CONNECTION_CTOR(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.numDownstreams()}, ${conn.maxNumPendingEvents}, ${conn.isPhysical});" - - private fun generateFederatedOutputSelfStruct(conn: UcFederatedGroupedConnection) = - if (conn.srcPort.type.isArray) - "LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_STRUCT_ARRAY(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.srcPort.type.id}, ${conn.srcPort.type.arrayLength});" - else - "LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_STRUCT(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.srcPort.type.toText()});" - - private fun generateFederatedOutputCtor(conn: UcFederatedGroupedConnection) = - "LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_CTOR(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.srcPort.type.toText()}, ${conn.getDestinationConnectionId()});" - - private fun generateFederatedConnectionSelfStruct(conn: UcFederatedGroupedConnection) = - if (conn.srcFed == currentFederate) generateFederatedOutputSelfStruct(conn) - else generateFederatedInputSelfStruct(conn) - - private fun generateFederatedConnectionCtor(conn: UcFederatedGroupedConnection) = - if (conn.srcFed == currentFederate) generateFederatedOutputCtor(conn) - else generateFederatedInputCtor(conn) - - private fun generateFederatedOutputInstance(conn: UcGroupedConnection) = - "LF_FEDERATED_OUTPUT_CONNECTION_INSTANCE(${reactor.codeType}, ${conn.getUniqueName()});" - - private fun generateFederatedInputInstance(conn: UcGroupedConnection) = - "LF_FEDERATED_INPUT_CONNECTION_INSTANCE(${reactor.codeType}, ${conn.getUniqueName()});" - - private fun generateFederatedConnectionInstance(conn: UcFederatedGroupedConnection) = - if (conn.srcFed == currentFederate) generateFederatedOutputInstance(conn) - else generateFederatedInputInstance(conn) - - private fun generateInitializeFederatedOutput(conn: UcFederatedGroupedConnection) = - "LF_INITIALIZE_FEDERATED_OUTPUT_CONNECTION(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.serializeFunc});" - - private fun generateInitializeFederatedInput(conn: UcFederatedGroupedConnection) = - "LF_INITIALIZE_FEDERATED_INPUT_CONNECTION(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.deserializeFunc});" - - private fun generateInitializeFederatedConnection(conn: UcFederatedGroupedConnection) = - if (conn.srcFed == currentFederate) generateInitializeFederatedOutput(conn) - else generateInitializeFederatedInput(conn) - - private fun generateReactorCtorCode(conn: UcGroupedConnection) = - if (conn.isLogical) { - "LF_INITIALIZE_LOGICAL_CONNECTION(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.bankWidth}, ${conn.portWidth})" - } else { - "LF_INITIALIZE_DELAYED_CONNECTION(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.delay}, ${conn.bankWidth}, ${conn.portWidth})" - } - - private fun generateFederateCtorCode(conn: UcFederatedConnectionBundle) = - "LF_INITIALIZE_FEDERATED_CONNECTION_BUNDLE(${conn.src.codeType}, ${conn.dest.codeType});" - - private fun generateConnectChannel( - groupedConn: UcGroupedConnection, - channel: UcConnectionChannel - ) = - """|lf_connect((Connection *) &self->${groupedConn.getUniqueName()}[${channel.src.getCodeBankIdx()}][${channel.src.getCodePortIdx()}], (Port *) ${channel.src.generateChannelPointer()}, (Port *) ${channel.dest.generateChannelPointer()}); - """ - .trimMargin() - - private fun generateConnectionStatements(conn: UcGroupedConnection) = - conn.channels.joinToString(separator = "\n") { generateConnectChannel(conn, it) } - - private fun generateConnectFederateOutputChannel( - bundle: UcFederatedConnectionBundle, - conn: UcFederatedGroupedConnection - ) = - conn.channels.joinWithLn { - "lf_connect_federated_output((Connection *)&self->LF_FEDERATED_CONNECTION_BUNDLE_NAME(${bundle.src.codeType}, ${bundle.dest.codeType}).${conn.getUniqueName()}, (Port*) &self->${it.src.federate!!.inst.name}[0].${it.src.varRef.name}[${it.src.portIdx}]);" - } - - private fun generateConnectFederateInputChannel( - bundle: UcFederatedConnectionBundle, - conn: UcGroupedConnection - ) = - conn.channels.joinWithLn { - "lf_connect_federated_input((Connection *)&self->LF_FEDERATED_CONNECTION_BUNDLE_NAME(${bundle.src.codeType}, ${bundle.dest.codeType}).${conn.getUniqueName()}, (Port*) &self->${it.dest.federate!!.inst.name}[0].${it.dest.varRef.name}[${it.dest.portIdx}]);" - } - - private fun generateFederateConnectionStatements(conn: UcFederatedConnectionBundle) = - conn.groupedConnections.joinWithLn { - if (it.srcFed == currentFederate) { - generateConnectFederateOutputChannel(conn, it) - } else { - generateConnectFederateInputChannel(conn, it) - } - } - - fun generateFederateCtorCodes() = - federatedConnectionBundles.joinToString( - prefix = "// Initialize connection bundles\n", separator = "\n", postfix = "\n") { - generateFederateCtorCode(it) - } + - federatedConnectionBundles.joinToString( - prefix = "// Do connections \n", separator = "\n", postfix = "\n") { - generateFederateConnectionStatements(it) - } - - fun generateReactorCtorCodes() = - nonFederatedConnections.joinToString( - prefix = "// Initialize connections\n", separator = "\n", postfix = "\n") { - generateReactorCtorCode(it) - } + - nonFederatedConnections.joinToString( - prefix = "// Do connections \n", separator = "\n", postfix = "\n") { - generateConnectionStatements(it) - } - - fun generateCtors() = - nonFederatedConnections.joinToString( - prefix = "// Connection constructors\n", separator = "\n", postfix = "\n") { - if (it.isDelayed) generateDelayedCtor(it) else generateLogicalCtor(it) - } - - fun generateSelfStructs() = - nonFederatedConnections.joinToString( - prefix = "// Connection structs\n", separator = "\n", postfix = "\n") { - if (it.isLogical) generateLogicalSelfStruct(it) else generateDelayedSelfStruct(it) - } - - private fun generateFederatedConnectionBundleSelfStruct(bundle: UcFederatedConnectionBundle) = - with(PrependOperator) { - """ |typedef struct { - | FederatedConnectionBundle super; - ${" | "..bundle.networkChannel.codeType} channel; - ${" | "..bundle.groupedConnections.joinWithLn { generateFederatedConnectionInstance(it) }} - | LF_FEDERATED_CONNECTION_BUNDLE_BOOKKEEPING_INSTANCES(${bundle.numInputs(currentFederate!!)}, ${ - bundle.numOutputs( - currentFederate - ) - }); - |} LF_FEDERATED_CONNECTION_BUNDLE_TYPE(${bundle.src.codeType}, ${bundle.dest.codeType}); - | - """ - .trimMargin() - } - - private fun generateFederatedConnectionBundleCtor(bundle: UcFederatedConnectionBundle) = - with(PrependOperator) { - """ |LF_FEDERATED_CONNECTION_BUNDLE_CTOR_SIGNATURE(${bundle.src.codeType}, ${bundle.dest.codeType}) { - | LF_FEDERATED_CONNECTION_BUNDLE_CTOR_PREAMBLE(); - | ${bundle.generateNetworkChannelCtor(currentFederate!!)} - | LF_FEDERATED_CONNECTION_BUNDLE_CALL_CTOR(); - ${" | "..bundle.groupedConnections.joinWithLn { generateInitializeFederatedConnection(it) }} - |} - """ - .trimMargin() - } - - fun generateFederatedSelfStructs() = - federatedConnectionBundles.joinToString( - prefix = "// Federated Connections\n", separator = "\n", postfix = "\n") { itOuter -> - itOuter.groupedConnections.joinToString( - prefix = "// Federated input and output connection self structs\n", - separator = "\n", - postfix = "\n") { - generateFederatedConnectionSelfStruct(it) - } + generateFederatedConnectionBundleSelfStruct(itOuter) - } - - fun generateFederatedCtors() = - federatedConnectionBundles.joinToString( - prefix = "// Federated Connections\n", separator = "\n", postfix = "\n") { itOuter -> - itOuter.groupedConnections.joinToString( - prefix = "// Federated input and output connection constructors\n", - separator = "\n", - postfix = "\n") { - generateFederatedConnectionCtor(it) - } + generateFederatedConnectionBundleCtor(itOuter) - } - - fun generateFederateStructFields() = - federatedConnectionBundles.joinToString( - prefix = "// Federated Connections\n", separator = "\n", postfix = "\n") { - "LF_FEDERATED_CONNECTION_BUNDLE_INSTANCE(${it.src.codeType}, ${it.dest.codeType});" - } - - fun generateReactorStructFields() = - nonFederatedConnections.joinToString( - prefix = "// Connections \n", separator = "\n", postfix = "\n") { - if (it.isLogical) - "LF_LOGICAL_CONNECTION_INSTANCE(${reactor.codeType}, ${it.getUniqueName()}, ${it.bankWidth}, ${it.portWidth});" - else - "LF_DELAYED_CONNECTION_INSTANCE(${reactor.codeType}, ${it.getUniqueName()}, ${it.bankWidth}, ${it.portWidth});" - } - - fun getMaxNumPendingEvents(): Int { - var res = 0 - for (conn in nonFederatedConnections) { - if (!conn.isLogical) { - res += conn.maxNumPendingEvents - } - } - for (bundle in federatedConnectionBundles) { - for (conn in bundle.groupedConnections) { - if (conn.destFed == currentFederate) { - res += conn.maxNumPendingEvents - } - } - } - return res - } - - fun generateNetworkChannelIncludes(): String = - federatedConnectionBundles - .distinctBy { it.networkChannel.type } - .joinWithLn { it.networkChannel.src.iface.includeHeaders } - - // Finds the longest path through the federation. Performs - // two breadt-first-searches looking for the longest path. - // see: https://www.geeksforgeeks.org/longest-path-undirected-tree/ - fun getLongestFederatePath(): Int { - data class Graph(val nodes: Int, val adj: List>) - - // Return the furthest node and its distance from u - fun breadthFirstSearch(u: Int, graph: Graph): Pair { - val visited = allFederates.map { false }.toMutableList() - val distance = allFederates.map { -1 }.toMutableList() - distance[u] = 0 - visited[u] = true - val queue: Queue = LinkedList() - queue.add(u) - - while (queue.isNotEmpty()) { - val front = queue.poll() - for (i in graph.adj[front]) { - if (!visited[i]) { - visited[i] = true - distance[i] = distance[front] + 1 - queue.add(i) - } - } - } - var maxDist = -1 - var nodeIdx = -1 - for (i in 0.. maxDist) { - maxDist = distance[i] - nodeIdx = i - } - } - return Pair(nodeIdx, maxDist) - } - // Build adjacency matrix - val adjacency = allFederates.map { mutableSetOf() } - for (bundle in allFederatedConnectionBundles) { - val src = allFederates.indexOf(bundle.src) - val dest = allFederates.indexOf(bundle.dest) - adjacency[src].add(dest) - adjacency[dest].add(src) - } - val graph = Graph(allFederates.size, adjacency) - val firstEndPoint = breadthFirstSearch(0, graph) - val actualLength = breadthFirstSearch(firstEndPoint.first, graph) - return actualLength.second - } - - fun areFederatesFullyConnected(): Boolean { - data class Graph(val nodes: Int, val adj: List>) - - // Return the furthest node and its distance from u - fun breadthFirstSearch(u: Int, graph: Graph): Boolean { - val visited = allFederates.map { false }.toMutableList() - visited[u] = true - val queue: Queue = LinkedList() - queue.add(u) - - while (queue.isNotEmpty()) { - val front = queue.poll() - for (i in graph.adj[front]) { - if (!visited[i]) { - visited[i] = true - queue.add(i) - } - } - } - return visited.all { it } - } - - // Build adjacency matrix - val adjacency = allFederates.map { mutableSetOf() } - for (bundle in allFederatedConnectionBundles) { - val src = allFederates.indexOf(bundle.src) - val dest = allFederates.indexOf(bundle.dest) - adjacency[src].add(dest) - adjacency[dest].add(src) - } - val graph = Graph(allFederates.size, adjacency) - return breadthFirstSearch(0, graph) - } -} diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionUtils.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionUtils.kt deleted file mode 100644 index b8f2f37ee..000000000 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionUtils.kt +++ /dev/null @@ -1,211 +0,0 @@ -package org.lflang.generator.uc - -import org.lflang.AttributeUtils -import org.lflang.TimeValue -import org.lflang.generator.orNever -import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeWidth -import org.lflang.generator.uc.UcInstanceGenerator.Companion.width -import org.lflang.generator.uc.UcPortGenerator.Companion.width -import org.lflang.lf.Connection -import org.lflang.lf.Port -import org.lflang.lf.VarRef - -/** - * A UcConnectionChannel is the fundamental lowest-level representation of a connection in a LF - * program. It connects two UcChannels, one at the source and one at the destination. - */ -class UcConnectionChannel(val src: UcChannel, val dest: UcChannel, val conn: Connection) { - val isFederated = - (src.federate != null) && (dest.federate != null) && (src.federate != dest.federate) - - /** - * Get the NetworkChannelType of this connection. If we are not in a federated program it is NONE - */ - fun getChannelType(): NetworkChannelType { - val linkAttr = AttributeUtils.getLinkAttribute(conn) - return if (linkAttr == null) { - src.federate?.getDefaultInterface()?.type ?: NetworkChannelType.NONE - } else { - val srcIf = linkAttr.getParamString("left") - if (srcIf == null) { - src.federate?.getDefaultInterface()?.type ?: NetworkChannelType.NONE - } else { - src.federate?.getInterface(srcIf)?.type ?: NetworkChannelType.NONE - } - } - } -} - -/** - * A GroupedConnection is a set of ConnectionChannels that can be grouped together for efficiency. - * All ConnectionChannels that start from the same LF port, either because of multiports, banks, or - * multiple connections. Are grouped. - * - * TODO: Give a better exaplanation for what a GroupedConnection is. - */ -open class UcGroupedConnection( - val src: VarRef, - val channels: List, - val lfConn: Connection, -) { - val delay = lfConn.delay.orNever().toCCode() - val isPhysical = lfConn.isPhysical - val isLogical = !lfConn.isPhysical && lfConn.delay == null - val srcInst = src.container - val srcPort = src.variable as Port - val isDelayed = lfConn.isPhysical || !isLogical // We define physical connections as delayed. - - private var uid: Int = -1 - - val bankWidth = srcInst?.codeWidth ?: 1 - val portWidth = srcPort.width - val numDownstreams = { - val frequencyMap = - channels.groupingBy { Pair(it.src.getCodePortIdx(), it.src.getCodeBankIdx()) }.eachCount() - frequencyMap.values.maxOrNull() ?: 0 - } - val maxNumPendingEvents = - if (AttributeUtils.getConnectionBufferSize(lfConn) > 0) - AttributeUtils.getConnectionBufferSize(lfConn) - else 1 - - fun assignUid(id: Int) { - uid = id - } - - fun getUniqueName() = "conn_${srcInst?.name ?: ""}_${srcPort.name}_${uid}" -} - -/** - * In federated programs, we must group connections differently. For a non federated program. A - * single output port connected to N different input ports could be grouped to a single - * GroupedConnection. For a federated program, we would need N GroupedConnections if an output port - * goes to N different federates. Moreover, if there are several kinds of NetworkChannels in the - * program, then we can only group connections transported over the same channels. - */ -class UcFederatedGroupedConnection( - src: VarRef, - channels: List, - lfConn: Connection, - val srcFed: UcFederate, - val destFed: UcFederate, -) : UcGroupedConnection(src, channels, lfConn) { - - // FIXME: Allow user to override and provide these. - val serializeFunc = "serialize_payload_default" - val deserializeFunc = "deserialize_payload_default" - - private var bundle: UcFederatedConnectionBundle? = null - - fun setBundle(bundle: UcFederatedConnectionBundle) { - this.bundle = bundle - } - - fun getMaxWait(): TimeValue = AttributeUtils.getMaxWait(lfConn) - - // THe connection index of this FederatedGroupedConnection is the index - // which it will appear in the destination UcFederatedConnectionBundle. - fun getDestinationConnectionId(): Int { - require(bundle != null) - val destInputConnections = bundle!!.groupedConnections.filter { it.destFed == destFed } - val index = destInputConnections.indexOf(this) - return index - } -} - -/** - * A FederatedConnectionBundle will contain all GroupedConnections going between two federates, in - * either direction. It also contains a NetworkChannel connecting and NetworkEndpoint in each - * federate. - */ -class UcFederatedConnectionBundle( - val src: UcFederate, - val dest: UcFederate, - val groupedConnections: List -) { - - init { - // Tell the grouped connections which bundle they are within. - groupedConnections.forEach { it.setBundle(this) } - } - - val networkChannel: UcNetworkChannel = - UcNetworkChannel.createNetworkEndpointsAndChannelForBundle(this) - - fun numOutputs(federate: UcFederate) = groupedConnections.count { it.srcFed == federate } - - fun numInputs(federate: UcFederate) = groupedConnections.count { it.destFed == federate } - - fun generateNetworkChannelCtor(federate: UcFederate): String = - if (federate == src) { - networkChannel.generateChannelCtorSrc() - } else { - networkChannel.generateChannelCtorDest() - } -} - -/** - * An UcChannel represents a single channel of an LF Port. Due to Multiports and Banks, each LF Port - * can have multiple channels. - */ -class UcChannel(val varRef: VarRef, val portIdx: Int, val bankIdx: Int, val federate: UcFederate?) { - fun getCodePortIdx() = portIdx - - fun getCodeBankIdx() = if (federate == null) bankIdx else 0 - - private val portOfContainedReactor = varRef.container != null - private val reactorInstance = - if (portOfContainedReactor) "${varRef.container.name}[${getCodeBankIdx()}]." else "" - private val portInstance = "${varRef.name}[${getCodePortIdx()}]" - - fun generateChannelPointer() = "&self->${reactorInstance}${portInstance}" -} - -/** - * This is a convenience-wrapper around a LF Port. It will construct UcChannels corresponding to the - * multiports and banks. - * - * If this is a federates program. it must be passed a list of federates associated with the LF - * Port. It is a list in case it is a bank. Then each federate of the bank must be passed to the - * constructor. - */ -class UcChannelQueue(varRef: VarRef, federates: List) { - private val bankWidth = varRef.container?.width ?: 1 - private val portWidth = (varRef.variable as Port).width - private val isInterleaved = varRef.isInterleaved - /** A queue of UcChannels that can be popped of as we create UcConnetions */ - private val channels = ArrayDeque() - - // Construct the stack of channels belonging to this port. If this port is interleaved, - // then we create channels first for ports then for banks. - init { - if (isInterleaved) { - for (i in 0.. = takeChannels(channels.size) - - // Get a number of channels from this port. This has sideeffects and will remove these - // channels from the port. - fun takeChannels(numChannels: Int): List { - assert(numChannels >= channels.size) - val res = mutableListOf() - for (i in 1..numChannels) { - res.add(channels.removeFirst()) - } - return res - } - - fun channelsLeft(): Int = channels.size -} 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 deleted file mode 100644 index c619359de..000000000 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcExtensions.kt +++ /dev/null @@ -1,39 +0,0 @@ -package org.lflang.generator.uc - -import org.lflang.* -import org.lflang.lf.* - -fun TimeValue.toCCode() = UcTypes.getTargetTimeExpr(this) - -fun Expression.toCCode(inferredType: InferredType? = null): String = - UcTypes.getTargetExpr(this, inferredType) - -fun Expression?.toCTime(): String = this?.toCCode(inferredType = InferredType.time()) ?: "0" - -val InferredType.CType - get() = UcTypes.getTargetType(this) - -val VarRef.name: String - get() = this.variable.name - -val TriggerRef.name: String - get() = - when (this) { - is VarRef -> this.name - is BuiltinTriggerRef -> type.literal - else -> unreachable() - } - -fun Attribute.getParamString(param: String): String? = - attrParms.find { it.name == param }?.value?.trim('"') - -fun Attribute.getParamInt(param: String): Int? = attrParms.find { it.name == param }?.value?.toInt() - -fun Attribute.getParamBigInt(param: String): Long? = - attrParms.find { it.name == param }?.value?.toLong() - -fun Attribute.getParamBool(param: String): Boolean? = - attrParms.find { it.name == param }?.value?.toBoolean() - -fun Attribute.getParamFloat(param: String): Double? = - attrParms.find { it.name == param }?.value?.toDouble() 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 deleted file mode 100644 index b38984627..000000000 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFileConfig.kt +++ /dev/null @@ -1,42 +0,0 @@ -package org.lflang.generator.uc - -import java.io.IOException -import java.nio.file.Path -import org.eclipse.emf.ecore.resource.Resource -import org.lflang.FileConfig -import org.lflang.lf.Reactor -import org.lflang.name -import org.lflang.util.FileUtil - -class UcFileConfig( - resource: Resource, - srcGenBasePath: Path, - useHierarchicalBin: Boolean, - runtimeSymlink: Boolean -) : FileConfig(resource, srcGenBasePath, useHierarchicalBin, runtimeSymlink) { - - @Throws(IOException::class) - override fun doClean() { - super.doClean() - this.ucBuildDirectories.forEach { FileUtil.deleteDirectory(it) } - } - - 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 = this.getDirectory(r).resolve(r.name) - - /** Path to the header file with preambles defined for this reactor */ - fun getPreambleHeaderPath(r: Resource): Path = getGenDir(r).resolve("_lf_preamble.h") - - /** Path to the source file corresponding to this reactor (needed for non generic reactors) */ - fun getReactorSourcePath(r: Reactor): Path = getGenDir(r.eResource()).resolve("${r.name}.c") - - /** 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 deleted file mode 100644 index ce9bb30f9..000000000 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt +++ /dev/null @@ -1,125 +0,0 @@ -package org.lflang.generator.uc - -import java.nio.file.Path -import org.apache.commons.lang3.tuple.MutablePair -import org.eclipse.emf.ecore.EObject -import org.eclipse.emf.ecore.resource.Resource -import org.eclipse.xtext.xbase.lib.IteratorExtensions -import org.lflang.allInstantiations -import org.lflang.allReactions -import org.lflang.generator.* -import org.lflang.generator.uc.UcInstanceGenerator.Companion.width -import org.lflang.generator.uc.UcReactorGenerator.Companion.hasStartup -import org.lflang.lf.Instantiation -import org.lflang.lf.Reactor -import org.lflang.reactor -import org.lflang.scoping.LFGlobalScopeProvider -import org.lflang.target.Target -import org.lflang.target.property.* - -/** Creates either a Federated or NonFederated generator depending on the type of LF program */ -fun createUcGenerator( - context: LFGeneratorContext, - scopeProvider: LFGlobalScopeProvider -): UcGenerator { - val nodes: Iterable = - IteratorExtensions.toIterable(context.getFileConfig().resource.getAllContents()) - for (reactor in nodes.filterIsInstance()) { - if (reactor.isFederated) { - return UcGeneratorFederated(context, scopeProvider) - } - } - return UcGeneratorNonFederated(context, scopeProvider) -} - -@Suppress("unused") -abstract class UcGenerator( - val context: LFGeneratorContext, - protected val scopeProvider: LFGlobalScopeProvider -) : GeneratorBase(context) { - - // keep a list of all source files we generate - val ucSources = mutableListOf() - val codeMaps = mutableMapOf() - - val fileConfig: UcFileConfig = context.fileConfig as UcFileConfig - val platform = targetConfig.get(PlatformProperty.INSTANCE) - - // Contains the maximum number of pending events required by each reactor. - // Is updated as reactors are analyzed and code-generated. - val maxNumPendingEvents = mutableMapOf() - - // Compute the total number of events and reactions within an instance (and its children) - // Also returns whether there is any startup event within the instance. - private fun totalNumEventsAndReactions(inst: Instantiation): Triple { - var numEvents = 0 - var numReactions = 0 - var hasStartup = false - val remaining = mutableListOf() - remaining.addAll(inst.reactor.allInstantiations) - while (remaining.isNotEmpty()) { - val child = remaining.removeFirst() - val childRes = totalNumEventsAndReactions(child) - - numEvents += childRes.first * child.width - numReactions += childRes.second * child.width - hasStartup = hasStartup or childRes.third - } - numEvents += maxNumPendingEvents[inst.reactor]!! - numReactions += inst.reactor.allReactions.size - hasStartup = hasStartup or inst.reactor.hasStartup - return Triple(numEvents, numReactions, hasStartup) - } - - // Compute the total number of events and reactions for a top-level reactor. - fun totalNumEventsAndReactions(main: Reactor): Pair { - val res = MutablePair(maxNumPendingEvents[main]!!, main.allReactions.size) - var hasStartup = main.hasStartup - for (inst in main.allInstantiations) { - val childRes = totalNumEventsAndReactions(inst) - res.left += childRes.first * inst.width - res.right += childRes.second * inst.width - hasStartup = hasStartup or childRes.third - } - if (hasStartup) res.left += 1 - return res.toPair() - } - - companion object { - const val libDir = "/lib/c" - const val MINIMUM_CMAKE_VERSION = "3.5" - } - - // Returns a possibly empty list of the federates in the current program. - protected fun getAllFederates(): List { - val res = mutableListOf() - for (reactor in reactors) { - if (reactor.isFederated) { - res.addAll(reactor.allInstantiations) - } - } - return res - } - - // Returns a list of all instantiated reactors within a top-level reactor. - protected fun getAllInstantiatedReactors(top: Reactor): List { - val res = mutableListOf() - for (inst in top.allInstantiations) { - res.add(inst.reactor) - res.addAll(getAllInstantiatedReactors(inst.reactor)) - } - return res.distinct() - } - - protected fun getAllImportedResources(resource: Resource): Set { - val resources: MutableSet = scopeProvider.getImportedResources(resource) - val importedRresources = resources.subtract(setOf(resource)) - resources.addAll(importedRresources.map { getAllImportedResources(it) }.flatten()) - resources.add(resource) - return resources - } - - override fun getTarget() = Target.UC - - override fun getTargetTypes(): TargetTypes = UcTypes -} diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGeneratorNonFederated.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGeneratorNonFederated.kt deleted file mode 100644 index f91f7be6c..000000000 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGeneratorNonFederated.kt +++ /dev/null @@ -1,103 +0,0 @@ -package org.lflang.generator.uc - -import java.nio.file.Path -import org.eclipse.emf.ecore.resource.Resource -import org.lflang.generator.CodeMap -import org.lflang.generator.GeneratorResult -import org.lflang.generator.GeneratorUtils.canGenerate -import org.lflang.generator.LFGeneratorContext -import org.lflang.isGeneric -import org.lflang.lf.Reactor -import org.lflang.reactor -import org.lflang.scoping.LFGlobalScopeProvider -import org.lflang.target.property.NoCompileProperty -import org.lflang.target.property.type.PlatformType -import org.lflang.util.FileUtil - -class UcGeneratorNonFederated(context: LFGeneratorContext, scopeProvider: LFGlobalScopeProvider) : - UcGenerator(context, scopeProvider) { - fun doGenerateReactor( - resource: Resource, - context: LFGeneratorContext, - srcGenPath: Path, - ): GeneratorResult.Status { - if (!canGenerate(errorsOccurred(), mainDef, messageReporter, context)) - return GeneratorResult.Status.FAILED - - if (context.args.generateFedTemplates) { - messageReporter - .nowhere() - .error("Cannot generate federate templates for non-federated program") - return GeneratorResult.Status.FAILED - } - - // Generate header and source files for all instantiated reactors. - getAllInstantiatedReactors(mainDef.reactor).map { generateReactorFiles(it, srcGenPath) } - - // Generate header and source files for the main reactor. - generateReactorFiles(mainDef.reactor, srcGenPath) - - // Generate preambles for all reactors. - for (r in getAllImportedResources(resource)) { - val generator = UcPreambleGenerator(r, fileConfig, scopeProvider) - val headerFile = fileConfig.getPreambleHeaderPath(r) - val preambleCodeMap = CodeMap.fromGeneratedCode(generator.generateHeader()) - codeMaps[srcGenPath.resolve(headerFile)] = preambleCodeMap - FileUtil.writeToFile(preambleCodeMap.generatedCode, srcGenPath.resolve(headerFile), true) - } - return GeneratorResult.Status.GENERATED - } - - override fun doGenerate(resource: Resource, context: LFGeneratorContext) { - super.doGenerate(resource, context) - - if (getAllFederates().isNotEmpty()) { - context.errorReporter.nowhere().error("Federated program detected in non-federated generator") - return - } - - super.copyUserFiles(targetConfig, fileConfig) - - val res = - doGenerateReactor( - resource, - context, - fileConfig.srcGenPath, - ) - - if (res == GeneratorResult.Status.GENERATED) { - // generate platform specific files - val platformGenerator = UcPlatformGeneratorNonFederated(this, fileConfig.srcGenPath) - platformGenerator.generatePlatformFiles() - - if (platform.platform == PlatformType.Platform.NATIVE && - !targetConfig.get(NoCompileProperty.INSTANCE)) { - if (platformGenerator.doCompile(context)) { - context.finish(GeneratorResult.Status.COMPILED, codeMaps) - } else { - context.finish(GeneratorResult.Status.FAILED, codeMaps) - } - } else { - context.finish(GeneratorResult.GENERATED_NO_EXECUTABLE.apply(context, codeMaps)) - } - } else { - return - } - } - - fun generateReactorFiles(reactor: Reactor, srcGenPath: Path) { - val generator = UcReactorGenerator(reactor, fileConfig, messageReporter) - maxNumPendingEvents.set(reactor, generator.getMaxNumPendingEvents()) - - val headerFile = fileConfig.getReactorHeaderPath(reactor) - val sourceFile = fileConfig.getReactorSourcePath(reactor) - val reactorCodeMap = CodeMap.fromGeneratedCode(generator.generateSource()) - if (!reactor.isGeneric) ucSources.add(sourceFile) - codeMaps[srcGenPath.resolve(sourceFile)] = reactorCodeMap - val headerCodeMap = CodeMap.fromGeneratedCode(generator.generateHeader()) - codeMaps[srcGenPath.resolve(headerFile)] = headerCodeMap - - FileUtil.writeToFile(headerCodeMap.generatedCode, srcGenPath.resolve(headerFile), true) - FileUtil.writeToFile(reactorCodeMap.generatedCode, srcGenPath.resolve(sourceFile), true) - } -} 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 deleted file mode 100644 index cd990c5b0..000000000 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt +++ /dev/null @@ -1,212 +0,0 @@ -package org.lflang.generator.uc - -import org.lflang.generator.PrependOperator -import org.lflang.generator.uc.UcReactorGenerator.Companion.codeType -import org.lflang.generator.uc.UcReactorGenerator.Companion.hasPhysicalActions -import org.lflang.lf.Reactor -import org.lflang.reactor -import org.lflang.target.TargetConfig -import org.lflang.target.property.FastProperty -import org.lflang.target.property.KeepaliveProperty -import org.lflang.target.property.TimeOutProperty -import org.lflang.toUnixString - -abstract class UcMainGenerator( - val targetConfig: TargetConfig, - val numEvents: Int, - val numReactions: Int -) { - abstract fun generateStartSource(): String - - val eventQueueName = "Main_EventQueue" - val systemEventQueueName = "Main_SystemEventQueue" - val reactionQueueName = "Main_ReactionQueue" - - abstract fun getNumSystemEvents(): Int - - abstract fun keepAlive(): Boolean - - fun generateDefineScheduler() = - """ - |static DynamicScheduler _scheduler; - |static Scheduler* scheduler = &_scheduler.super; - """ - .trimMargin() - - fun generateIncludeScheduler() = """#include "reactor-uc/schedulers/dynamic/scheduler.h" """ - - open fun generateInitializeScheduler() = - "DynamicScheduler_ctor(&_scheduler, _lf_environment, &${eventQueueName}.super, &${systemEventQueueName}.super, &${reactionQueueName}.super, ${getDuration()}, ${keepAlive()});" - - fun getDuration() = - if (targetConfig.isSet(TimeOutProperty.INSTANCE)) - targetConfig.get(TimeOutProperty.INSTANCE).toCCode() - else "FOREVER" - - fun fast() = if (targetConfig.isSet(FastProperty.INSTANCE)) "true" else "false" - - fun generateDefineQueues() = - with(PrependOperator) { - """ - |// Define queues used by scheduler - |LF_DEFINE_EVENT_QUEUE(${eventQueueName}, ${numEvents}) - |LF_DEFINE_EVENT_QUEUE(${systemEventQueueName}, ${getNumSystemEvents()}) - |LF_DEFINE_REACTION_QUEUE(${reactionQueueName}, ${numReactions}) - """ - .trimMargin() - } - - fun generateInitializeQueues() = - with(PrependOperator) { - """ - |// Define queues used by scheduler - |LF_INITIALIZE_EVENT_QUEUE(${eventQueueName}, ${numEvents}) - |LF_INITIALIZE_EVENT_QUEUE(${systemEventQueueName}, ${getNumSystemEvents()}) - |LF_INITIALIZE_REACTION_QUEUE(${reactionQueueName}, ${numReactions}) - """ - .trimMargin() - } - - fun generateStartHeader() = - with(PrependOperator) { - """ - |#ifndef REACTOR_UC_LF_MAIN_H - |#define REACTOR_UC_LF_MAIN_H - | - |void lf_start(void); - | - |#endif - | - """ - .trimMargin() - } - - fun generateMainSource() = - with(PrependOperator) { - """ - |#include "lf_start.h" - |int main(void) { - | lf_start(); - | return 0; - |} - """ - .trimMargin() - } -} - -class UcMainGeneratorNonFederated( - private val main: Reactor, - targetConfig: TargetConfig, - numEvents: Int, - numReactions: Int, - private val fileConfig: UcFileConfig, -) : UcMainGenerator(targetConfig, numEvents, numReactions) { - - private val ucParameterGenerator = UcParameterGenerator(main) - - override fun getNumSystemEvents(): Int = 0 - - override fun keepAlive(): Boolean { - if (targetConfig.isSet(KeepaliveProperty.INSTANCE)) { - return targetConfig.get(KeepaliveProperty.INSTANCE) - } else { - return main.hasPhysicalActions() - } - } - - override fun generateStartSource() = - with(PrependOperator) { - """ - |#include "reactor-uc/reactor-uc.h" - ${" |"..generateIncludeScheduler()} - |#include "${fileConfig.getReactorHeaderPath(main).toUnixString()}" - |static ${main.codeType} main_reactor; - |static Environment lf_environment; - |Environment *_lf_environment = &lf_environment; - ${" |"..generateDefineQueues()} - ${" |"..generateDefineScheduler()} - |void lf_exit(void) { - | Environment_free(&lf_environment); - |} - |void lf_start(void) { - ${" | "..generateInitializeQueues()} - ${" | "..generateInitializeScheduler()} - | Environment_ctor(&lf_environment, (Reactor *)&main_reactor, scheduler, ${fast()}); - | ${main.codeType}_ctor(&main_reactor, NULL, _lf_environment ${ucParameterGenerator.generateReactorCtorDefaultArguments()}); - | _lf_environment->assemble(_lf_environment); - | _lf_environment->start(_lf_environment); - | lf_exit(); - |} - """ - .trimMargin() - } -} - -class UcMainGeneratorFederated( - private val currentFederate: UcFederate, - private val otherFederates: List, - targetConfig: TargetConfig, - numEvents: Int, - numReactions: Int, - private val fileConfig: UcFileConfig, -) : UcMainGenerator(targetConfig, numEvents, numReactions) { - private val top = currentFederate.inst.eContainer() as Reactor - private val main = currentFederate.inst.reactor - private val ucConnectionGenerator = UcConnectionGenerator(top, currentFederate, otherFederates) - private val netBundlesSize = ucConnectionGenerator.getNumFederatedConnectionBundles() - private val clockSyncGenerator = - UcClockSyncGenerator(currentFederate, ucConnectionGenerator, targetConfig) - private val longestPath = 0 - - override fun getNumSystemEvents(): Int { - val clockSyncSystemEvents = UcClockSyncGenerator.getNumSystemEvents(netBundlesSize) - val startupCoordinatorEvents = UcStartupCoordinatorGenerator.getNumSystemEvents(netBundlesSize) - return clockSyncSystemEvents + startupCoordinatorEvents - } - - override fun keepAlive(): Boolean { - if (targetConfig.isSet(KeepaliveProperty.INSTANCE)) { - return targetConfig.get(KeepaliveProperty.INSTANCE) - } else { - if (main.inputs.isNotEmpty()) { - return true - } else if (top.hasPhysicalActions()) { - return true - } else { - return false - } - } - } - - override fun generateInitializeScheduler() = - "DynamicScheduler_ctor(&_scheduler, _lf_environment, &${eventQueueName}.super, &${systemEventQueueName}.super, &${reactionQueueName}.super, ${getDuration()}, ${keepAlive()});" - - override fun generateStartSource() = - with(PrependOperator) { - """ - |#include "reactor-uc/reactor-uc.h" - ${" |"..generateIncludeScheduler()} - |#include "lf_federate.h" - |static ${currentFederate.codeType} main_reactor; - |static FederatedEnvironment lf_environment; - |Environment *_lf_environment = &lf_environment.super; - ${" |"..generateDefineQueues()} - ${" |"..generateDefineScheduler()} - |void lf_exit(void) { - | FederatedEnvironment_free(&lf_environment); - |} - |void lf_start(void) { - ${" | "..generateInitializeQueues()} - ${" | "..generateInitializeScheduler()} - | FederatedEnvironment_ctor(&lf_environment, (Reactor *)&main_reactor, scheduler, ${fast()}, - | (FederatedConnectionBundle **) &main_reactor._bundles, ${netBundlesSize}, &main_reactor.${UcStartupCoordinatorGenerator.instName}.super, - | ${if (clockSyncGenerator.enabled()) "&main_reactor.${UcClockSyncGenerator.instName}.super" else "NULL"}); - | ${currentFederate.codeType}_ctor(&main_reactor, NULL, _lf_environment); - | _lf_environment->assemble(_lf_environment); - | _lf_environment->start(_lf_environment); - | lf_exit(); - |} - """ - .trimMargin() - } -} diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMakeGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMakeGenerator.kt deleted file mode 100644 index 53e921003..000000000 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMakeGenerator.kt +++ /dev/null @@ -1,68 +0,0 @@ -package org.lflang.generator.uc - -import java.nio.file.Path -import kotlin.io.path.name -import org.lflang.FileConfig -import org.lflang.generator.PrependOperator -import org.lflang.generator.PrependOperator.rangeTo -import org.lflang.joinWithLn -import org.lflang.lf.Reactor -import org.lflang.target.TargetConfig -import org.lflang.toUnixString - -abstract class UcMakeGenerator( - private val mainTarget: String, -) { - abstract fun generateMake(sources: List): String - - protected val S = '$' // a little trick to escape the dollar sign with $S - - fun doGenerateMake(sources: List, compileDefs: List) = - with(PrependOperator) { - val sources = sources.filterNot { it.name == "lf_main.c" } - """ - |# Makefile generated for ${mainTarget} - |LFC_GEN_SOURCES = \ - ${" | "..sources.joinWithLn { it.toUnixString() + if (it != sources.last()) " \\" else "" }} - |LFC_GEN_MAIN = lf_main.c - |LFC_GEN_COMPILE_DEFS = \ - ${" | "..compileDefs.joinWithLn { it + if (it != compileDefs.last()) " \\" else "" }} - |RUNTIME_PATH = $(CURDIR)/reactor-uc - """ - .trimMargin() - } -} - -class UcMakeGeneratorNonFederated( - private val main: Reactor, - private val targetConfig: TargetConfig, - private val fileConfig: FileConfig, -) : UcMakeGenerator(fileConfig.name) { - override fun generateMake(sources: List) = doGenerateMake(sources, emptyList()) -} - -class UcMakeGeneratorFederated( - private val federate: UcFederate, - targetConfig: TargetConfig, - fileConfig: UcFileConfig, -) : UcMakeGenerator(federate.codeType) { - override fun generateMake(sources: List): String { - val channelTypes = federate.interfaces.map { it.type }.toSet() - val channelTypesCompileDefs = - channelTypes.joinWithLn { - when (it) { - NetworkChannelType.TCP_IP -> "CFLAGS += -DNETWORK_CHANNEL_TCP" - NetworkChannelType.COAP_UDP_IP -> "CFLAGS += -DNETWORK_CHANNEL_COAP" - NetworkChannelType.UART -> "CFLAGS += -DNETWORK_CHANNEL_UART" - NetworkChannelType.NONE -> "" - NetworkChannelType.CUSTOM -> "" - } - } - - return """ - |${doGenerateMake(sources, federate.getCompileDefs())} - |${channelTypesCompileDefs} - """ - .trimMargin() - } -} 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 deleted file mode 100644 index 0c3c699cd..000000000 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcParameterGenerator.kt +++ /dev/null @@ -1,54 +0,0 @@ -package org.lflang.generator.uc - -import org.lflang.* -import org.lflang.lf.Instantiation -import org.lflang.lf.Parameter -import org.lflang.lf.Reactor - -class UcParameterGenerator(private val reactor: Reactor, private val federate: UcFederate? = null) { - - companion object { - - val Parameter.targetType - get(): String = this.inferredType.CType - - val Parameter.isPresentName - get(): String = "__${this.name}" - } - - fun generateReactorStructFields() = - reactor.allParameters.joinToString( - prefix = "// Reactor parameters\n", separator = "\n", postfix = "\n") { - "${it.inferredType.CType} ${it.name};" - } - - fun generateReactorCtorCodes() = - reactor.allParameters.joinToString( - prefix = "// Initialize Reactor parameters\n", separator = "\n", postfix = "\n") { - """| - |self->${it.name} = ${it.name}; - """ - .trimMargin() - } - - fun generateReactorCtorDefArguments() = - reactor.allParameters.joinToString(separator = "") { ", ${it.inferredType.CType} ${it.name}" } - - fun generateReactorCtorDefaultArguments() = - reactor.allParameters.joinToString(separator = "") { ", ${it.init.expr.toCCode()}" } - - fun generateReactorCtorDeclArguments(r: Instantiation) = - r.reactor.allParameters.joinToString(separator = "") { - if (it.name == "bank_idx" || it.name == "bank_index") { - if (federate != null) { - ", ${federate.bankIdx}" - } else { - ", i" - } - } else if (r.parameters.filter { p -> p.lhs.name == it.name }.isEmpty()) { - ", ${it.init.expr.toCCode()}" - } else { - ", ${r.parameters.find{ p -> p.lhs.name == it.name}!!.rhs.expr.toCCode()}" - } - } -} diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGenerator.kt deleted file mode 100644 index c1907c3d1..000000000 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGenerator.kt +++ /dev/null @@ -1,228 +0,0 @@ -package org.lflang.generator.uc - -import java.nio.file.Files -import java.nio.file.Path -import java.nio.file.Paths -import kotlin.io.path.* -import org.lflang.MessageReporter -import org.lflang.generator.CodeMap -import org.lflang.generator.GeneratorCommandFactory -import org.lflang.generator.LFGeneratorContext -import org.lflang.target.TargetConfig -import org.lflang.target.property.BuildTypeProperty -import org.lflang.target.property.type.BuildTypeType.BuildType -import org.lflang.toDefinition -import org.lflang.toUnixString -import org.lflang.util.FileUtil -import org.lflang.util.LFCommand - -/** Abstract class for generating platform specific files and invoking the target compiler. */ -abstract class UcPlatformGenerator(protected val generator: UcGenerator) { - private val codeMaps = generator.codeMaps - private val ucSources = generator.ucSources - protected val messageReporter: MessageReporter = generator.messageReporter - protected val fileConfig: UcFileConfig = generator.fileConfig - protected val targetConfig: TargetConfig = generator.targetConfig - private val commandFactory: GeneratorCommandFactory = generator.commandFactory - protected val mainReactor = generator.mainDef.reactorClass.toDefinition() - abstract val buildPath: Path - abstract val srcGenPath: Path - abstract val targetName: String - - private val relativeBinDir = fileConfig.outPath.relativize(fileConfig.binPath).toUnixString() - - abstract fun generatePlatformFiles() - - private val cmakeArgs: List - get() = - listOf( - "-DCMAKE_BUILD_TYPE=${targetConfig.get(BuildTypeProperty.INSTANCE)}", - ) - - companion object { - fun buildTypeToCmakeConfig(type: BuildType) = - when (type) { - BuildType.TEST -> "Debug" - else -> type.toString() - } - } - - @OptIn(ExperimentalPathApi::class) - fun doGeneratePlatformFiles( - mainGenerator: UcMainGenerator, - cmakeGenerator: UcCmakeGenerator, - makeGenerator: UcMakeGenerator - ) { - val reactorUCEnvPath = System.getenv("REACTOR_UC_PATH") - if (reactorUCEnvPath == null) { - messageReporter - .nowhere() - .error( - "REACTOR_UC_PATH environment variable not defined. Do source env.bash in reactor-uc") - return - } - val runtimePath: Path = Paths.get(reactorUCEnvPath) - - val startSourceFile = Paths.get("lf_start.c") - val startHeaderFile = Paths.get("lf_start.h") - val mainSourceFile = Paths.get("lf_main.c") - - val startCodeMap = CodeMap.fromGeneratedCode(mainGenerator.generateStartSource()) - val mainCodeMap = CodeMap.fromGeneratedCode(mainGenerator.generateMainSource()) - - ucSources.addAll(listOf(startSourceFile, mainSourceFile)) - codeMaps[srcGenPath.resolve(startSourceFile)] = startCodeMap - codeMaps[srcGenPath.resolve(mainSourceFile)] = mainCodeMap - - FileUtil.writeToFile(startCodeMap.generatedCode, srcGenPath.resolve(startSourceFile), true) - FileUtil.writeToFile(mainCodeMap.generatedCode, srcGenPath.resolve(mainSourceFile), true) - FileUtil.writeToFile( - mainGenerator.generateStartHeader(), srcGenPath.resolve(startHeaderFile), true) - - FileUtil.writeToFile( - cmakeGenerator.generateIncludeCmake(ucSources), srcGenPath.resolve("Include.cmake"), true) - FileUtil.writeToFile( - cmakeGenerator.generateMainCmakeNative(), srcGenPath.resolve("CMakeLists.txt"), true) - val runtimeDestinationPath: Path = srcGenPath.resolve("reactor-uc") - if (fileConfig.runtimeSymlink) { - if (runtimeDestinationPath.exists() && !runtimeDestinationPath.isSymbolicLink()) { - runtimeDestinationPath.deleteRecursively() - } - runtimeDestinationPath.createSymbolicLinkPointingTo(runtimePath) - } else { - if (runtimeDestinationPath.exists() && runtimeDestinationPath.isSymbolicLink()) { - runtimeDestinationPath.deleteIfExists() - } - val entriesToCopy = listOf("src", "include", "external", "cmake", "make", "CMakeLists.txt") - FileUtil.copyFilesOrDirectories( - entriesToCopy.map { runtimePath.resolve(it).toString() }, - runtimeDestinationPath, - fileConfig, - messageReporter, - false) - } - - FileUtil.writeToFile( - makeGenerator.generateMake(ucSources), srcGenPath.resolve("Makefile"), true) - } - - fun doCompile(context: LFGeneratorContext, onlyGenerateBuildFiles: Boolean = false): Boolean { - // make sure the build directory exists - Files.createDirectories(buildPath) - - val version = checkCmakeVersion() - var parallelize = true - if (version != null && version.compareVersion("3.12.0") < 0) { - messageReporter - .nowhere() - .warning("CMAKE is older than version 3.12. Parallel building is not supported.") - parallelize = false - } - - if (version != null) { - val cmakeReturnCode = runCmake(context) - - if (cmakeReturnCode == 0 && !onlyGenerateBuildFiles) { - // If cmake succeeded, run make - val makeCommand = createMakeCommand(buildPath, parallelize, targetName) - val makeReturnCode = - UcValidator(fileConfig, messageReporter, codeMaps) - .run(makeCommand, context.cancelIndicator) - var installReturnCode = 0 - if (makeReturnCode == 0) { - val installCommand = createMakeCommand(buildPath, parallelize, "install") - installReturnCode = installCommand.run(context.cancelIndicator) - if (installReturnCode == 0) { - println("SUCCESS (compiling generated C code)") - println("Generated source code is in ${fileConfig.srcGenPath}") - println("Compiled binary is in ${fileConfig.binPath}") - } - } - if ((makeReturnCode != 0 || installReturnCode != 0) && !messageReporter.errorsOccurred) { - // If errors occurred but none were reported, then the following message is the best we - // can do. - messageReporter.nowhere().error("make failed with error code $makeReturnCode") - } - } - if (cmakeReturnCode != 0) { - messageReporter.nowhere().error("cmake failed with error code $cmakeReturnCode") - } - } - return !messageReporter.errorsOccurred - } - - private fun checkCmakeVersion(): String? { - // get the installed cmake version and make sure it is at least 3.5 - val cmd = commandFactory.createCommand("cmake", listOf("--version"), buildPath) - var version: String? = null - if (cmd != null && cmd.run() == 0) { - val regex = "\\d+(\\.\\d+)+".toRegex() - version = regex.find(cmd.output.toString())?.value - } - if (version == null || version.compareVersion("3.5.0") < 0) { - messageReporter - .nowhere() - .error( - "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 - } - - return version - } - - private fun runCmake(context: LFGeneratorContext): Int { - val cmakeCommand = createCmakeCommand(buildPath, fileConfig.outPath) - return cmakeCommand.run(context.cancelIndicator) - } - - private fun String.compareVersion(other: String): Int { - val a = this.split(".").map { it.toInt() } - val b = other.split(".").map { it.toInt() } - for (x in (a zip b)) { - val res = x.first.compareTo(x.second) - if (res != 0) return res - } - return 0 - } - - private fun getMakeArgs(buildPath: Path, parallelize: Boolean, target: String): List { - val cmakeConfig = buildTypeToCmakeConfig(targetConfig.get(BuildTypeProperty.INSTANCE)) - val makeArgs = - mutableListOf( - "--build", buildPath.fileName.toString(), "--config", cmakeConfig, "--target", target) - - if (parallelize) { - makeArgs.addAll(listOf("--parallel", Runtime.getRuntime().availableProcessors().toString())) - } - - return makeArgs - } - - private fun createMakeCommand(buildPath: Path, parallelize: Boolean, target: String): LFCommand { - val makeArgs = getMakeArgs(buildPath, parallelize, target) - return commandFactory.createCommand("cmake", makeArgs, buildPath.parent) - } - - private fun getCmakeArgs(buildPath: Path, outPath: Path, sourcesRoot: String? = null) = - cmakeArgs + - listOf( - "-DCMAKE_INSTALL_PREFIX=${outPath.toUnixString()}", - "-DCMAKE_INSTALL_BINDIR=$relativeBinDir", - "--fresh", - "-S", - sourcesRoot ?: srcGenPath.toUnixString(), - "-B", - buildPath.fileName.toString()) - - private fun createCmakeCommand( - buildPath: Path, - outPath: Path, - sourcesRoot: String? = null - ): LFCommand { - val cmd = - commandFactory.createCommand( - "cmake", getCmakeArgs(buildPath, outPath, sourcesRoot), buildPath.parent) - return cmd - } -} diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorNonFederated.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorNonFederated.kt deleted file mode 100644 index d09c0bfa3..000000000 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorNonFederated.kt +++ /dev/null @@ -1,26 +0,0 @@ -package org.lflang.generator.uc - -import java.nio.file.Path -import org.lflang.reactor - -class UcPlatformGeneratorNonFederated(generator: UcGenerator, override val srcGenPath: Path) : - UcPlatformGenerator(generator) { - - override val buildPath = srcGenPath.resolve("build") - override val targetName = fileConfig.name - - override fun generatePlatformFiles() { - val numEventsAndReactions = generator.totalNumEventsAndReactions(generator.mainDef.reactor) - val mainGenerator = - UcMainGeneratorNonFederated( - mainReactor, - generator.targetConfig, - numEventsAndReactions.first, - numEventsAndReactions.second, - generator.fileConfig) - val cmakeGenerator = - UcCmakeGeneratorNonFederated(generator.mainDef, targetConfig, generator.fileConfig) - val makeGenerator = UcMakeGeneratorNonFederated(mainReactor, targetConfig, generator.fileConfig) - super.doGeneratePlatformFiles(mainGenerator, cmakeGenerator, makeGenerator) - } -} 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 deleted file mode 100644 index 1451c81a3..000000000 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt +++ /dev/null @@ -1,140 +0,0 @@ -/** - * ********** - * Copyright (c) 2019-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.uc.UcInstanceGenerator.Companion.codeWidth -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 connections: UcConnectionGenerator -) { - val Port.external_args - get(): String = "_${name}_external" - - public companion object { - val Port.width - get(): Int = widthSpec?.getWidth() ?: 1 - - val Type.isArray - get(): Boolean = cStyleArraySpec != null - - val Type.arrayLength - get(): Int = cStyleArraySpec.length - } - - private fun generateSelfStruct(input: Input): String { - if (input.type.isArray) { - return "LF_DEFINE_INPUT_ARRAY_STRUCT(${reactor.codeType}, ${input.name}, ${reactor.getEffects(input).size}, ${reactor.getObservers(input).size}, ${input.type.id}, ${input.type.arrayLength}, ${connections.getNumConnectionsFromPort(null, input as Port)});" - } else { - return "LF_DEFINE_INPUT_STRUCT(${reactor.codeType}, ${input.name}, ${reactor.getEffects(input).size}, ${reactor.getObservers(input).size}, ${input.type.toText()}, ${connections.getNumConnectionsFromPort(null, input as Port)});" - } - } - - private fun generateInputCtor(input: Input) = - "LF_DEFINE_INPUT_CTOR(${reactor.codeType}, ${input.name}, ${reactor.getEffects(input).size}, ${reactor.getObservers(input).size}, ${input.type.toText()}, ${connections.getNumConnectionsFromPort(null, input as Port)});" - - private fun generateSelfStruct(output: Output): String { - if (output.type.isArray) { - return "LF_DEFINE_OUTPUT_ARRAY_STRUCT(${reactor.codeType}, ${output.name}, ${reactor.getSources(output).size}, ${output.type.id}, ${output.type.arrayLength});" - } else { - return "LF_DEFINE_OUTPUT_STRUCT(${reactor.codeType}, ${output.name}, ${reactor.getSources(output).size}, ${output.type.toText()});" - } - } - - private fun generateOutputCtor(output: Output) = - "LF_DEFINE_OUTPUT_CTOR(${reactor.codeType}, ${output.name}, ${reactor.getSources(output).size});" - - fun generateSelfStructs() = - reactor.allInputs.plus(reactor.allOutputs).joinToString( - prefix = "// Port structs\n", separator = "\n", postfix = "\n") { - when (it) { - is Input -> generateSelfStruct(it) - is Output -> generateSelfStruct(it) - else -> "" - } - } - - fun generateReactorStructFields() = - reactor.allInputs.plus(reactor.allOutputs).joinToString( - prefix = "// Ports \n", separator = "\n", postfix = "\n") { - "LF_PORT_INSTANCE(${reactor.codeType}, ${it.name}, ${it.width});" - } - - fun generateCtors() = - reactor.allInputs.plus(reactor.allOutputs).joinToString( - prefix = "// Port constructors\n", separator = "\n", postfix = "\n") { - when (it) { - is Input -> generateInputCtor(it) - is Output -> generateOutputCtor(it) - else -> throw IllegalArgumentException("Error: Port was neither input nor output") - } - } - - private fun generateReactorCtorCode(input: Input) = - "LF_INITIALIZE_INPUT(${reactor.codeType}, ${input.name}, ${input.width}, ${input.external_args});" - - private fun generateReactorCtorCode(output: Output) = - "LF_INITIALIZE_OUTPUT(${reactor.codeType}, ${output.name}, ${output.width}, ${output.external_args});" - - private fun generateReactorCtorCode(port: Port) = - when (port) { - is Input -> generateReactorCtorCode(port) - is Output -> generateReactorCtorCode(port) - else -> throw IllegalArgumentException("Error: Port was neither input nor output") - } - - fun generateReactorCtorCodes() = - reactor.allInputs.plus(reactor.allOutputs).joinToString( - prefix = "// Initialize ports\n", separator = "\n", postfix = "\n") { - generateReactorCtorCode(it) - } - - fun generateDefineContainedOutputArgs(r: Instantiation) = - r.reactor.allOutputs.joinToString(separator = "\n", prefix = "\n", postfix = "\n") { - "LF_DEFINE_CHILD_OUTPUT_ARGS(${r.name}, ${it.name}, ${r.codeWidth}, ${it.width});" - } - - fun generateDefineContainedInputArgs(r: Instantiation) = - r.reactor.allInputs.joinToString(separator = "\n", prefix = "\n", postfix = "\n") { - "LF_DEFINE_CHILD_INPUT_ARGS(${r.name}, ${it.name}, ${r.codeWidth}, ${it.width});" - } - - fun generateReactorCtorDefArguments() = - reactor.allOutputs.joinToString(separator = "") { - ", OutputExternalCtorArgs *${it.external_args}" - } + - reactor.allInputs.joinToString(separator = "") { - ", InputExternalCtorArgs *${it.external_args}" - } - - fun generateReactorCtorDeclArguments(r: Instantiation) = - r.reactor.allOutputs.plus(r.reactor.allInputs).joinToString(separator = "") { - ", _${r.name}_${it.name}_args[i]" - } -} 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 deleted file mode 100644 index cc6e2b834..000000000 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactionGenerator.kt +++ /dev/null @@ -1,448 +0,0 @@ -package org.lflang.generator.uc - -import org.lflang.* -import org.lflang.generator.PrependOperator -import org.lflang.generator.orNever -import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeWidth -import org.lflang.generator.uc.UcPortGenerator.Companion.width -import org.lflang.generator.uc.UcReactorGenerator.Companion.codeType -import org.lflang.lf.* - -class UcReactionGenerator(private val reactor: Reactor) { - private val Reaction.codeName - get(): String = name ?: "reaction$index" - - private val Reaction.nameInReactor - get(): String = "self->${codeName}" - - val Reaction.index - get(): Int { - var idx = 0 - for (r in reactor.allReactions) { - if (this == r) { - break - } - idx += 1 - } - return idx - } - - private val Reaction.allUncontainedTriggers - get() = triggers.filterNot { it.isEffectOf(this) || it.isContainedRef } - - private val Reaction.ctorDeadlineArgs - get() = - if (deadline != null) - "LF_REACTION_TYPE(${reactor.codeType}, ${codeName}_deadline_violation_handler) " - else "NULL" - - private val Reaction.ctorStpArgs - get() = - if (tardy != null && tardy.code != null) - "LF_REACTION_TYPE(${reactor.codeType}, ${codeName}_stp_violation_handler)" - else "NULL" - - 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 reactionsWithDeadline = reactor.allReactions.filter { it.deadline != null } - private val reactionsWithTardyViolationHandler = - reactor.allReactions.filter { it.tardy != null && it.tardy.code != null } - - private val Reaction.allContainedTriggers - get() = triggers.filter { !it.isEffectOf(this) && it.isContainedRef } - - private val Reaction.allContainedSources - get() = sources.filter { !it.isEffectOf(this) && it.isContainedRef } - - // Calculate the total number of effects, considering that we might write to - // a contained input port - private val Reaction.totalNumEffects - get(): Int { - var res = 0 - for (effect in allUncontainedEffects) { - val variable = effect.variable - res += - if (variable is Port) { - variable.width - } else { - 1 - } - } - for (effect in allContainedEffects) { - res += effect.container.codeWidth * (effect.variable as Port).width - } - return res - } - - private val Reaction.allContainedEffectsTriggersAndSources - get() = run { - val res = mutableMapOf>() - for (varRef in allContainedEffects.plus(allContainedSources).plus(allContainedTriggers)) { - val varRef = varRef as VarRef - if (varRef.container!! !in res.keys) { - res[varRef.container] = mutableListOf() - } - res[varRef.container] = res[varRef.container]!!.plus(varRef) - } - res - } - - 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 -> - "LF_SCOPE_STARTUP(${reactor.codeType});" - this is BuiltinTriggerRef && this.type == BuiltinTrigger.SHUTDOWN -> - "LF_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 -> "LF_SCOPE_TIMER(${reactor.codeType}, ${name});" - is Action -> "LF_SCOPE_ACTION(${reactor.codeType}, ${name});" - is Port -> { - if (variable.width > 1) { - "LF_SCOPE_MULTIPORT(${reactor.codeType}, ${name});" - } else { - "LF_SCOPE_PORT(${reactor.codeType}, ${name});" - } - } - else -> throw AssertionError("Unexpected variable 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 registerPortSource(varRef: VarRef, port: Port, reaction: Reaction) = - if (varRef.container != null) { - (0..${varRef.container.name}[${it}].${port.name}, ${reaction.nameInReactor}, ${port.width})" - } - } else { - "LF_PORT_REGISTER_SOURCE(self->${varRef.name}, ${reaction.nameInReactor}, ${port.width});" - } - - private fun registerSource(varRef: VarRef, reaction: Reaction) = - when (val variable = varRef.variable) { - is Action -> "LF_ACTION_REGISTER_SOURCE(self->${varRef.name}, ${reaction.nameInReactor});" - is Port -> registerPortSource(varRef, variable, reaction) - else -> throw AssertionError("Unexpected variable type $varRef") - } - - private fun registerEffect(triggerRef: TriggerRef, reaction: Reaction) = - when { - triggerRef is BuiltinTriggerRef && triggerRef.type == BuiltinTrigger.STARTUP -> - "LF_STARTUP_REGISTER_EFFECT(${reaction.nameInReactor});" - triggerRef is BuiltinTriggerRef && triggerRef.type == BuiltinTrigger.SHUTDOWN -> - "LF_SHUTDOWN_REGISTER_EFFECT(${reaction.nameInReactor});" - triggerRef is VarRef -> registerEffect(triggerRef, reaction) - else -> throw AssertionError("Unexpected variable type") - } - - private fun registerPortEffect(varRef: VarRef, port: Port, reaction: Reaction) = - if (varRef.container != null) { - (0..${varRef.container.name}[${it}].${port.name}, ${reaction.nameInReactor}, ${port.width})" - } - } else { - "LF_PORT_REGISTER_EFFECT(self->${varRef.name}, ${reaction.nameInReactor}, ${port.width});" - } - - private fun registerEffect(varRef: VarRef, reaction: Reaction) = - when (val variable = varRef.variable) { - is Timer -> "LF_TIMER_REGISTER_EFFECT(self->${varRef.name}, ${reaction.nameInReactor});" - is Action -> "LF_ACTION_REGISTER_EFFECT(self->${varRef.name}, ${reaction.nameInReactor});" - is Port -> registerPortEffect(varRef, variable, reaction) - else -> throw AssertionError("Unexpected variable type") - } - - private fun registerPortObserver(varRef: VarRef, port: Port, reaction: Reaction) = - if (varRef.container != null) { - (0..${varRef.container.name}[${it}].${port.name}, ${reaction.nameInReactor}, ${port.width})" - } - } else { - "LF_PORT_REGISTER_OBSERVER(self->${varRef.name}, ${reaction.nameInReactor}, ${port.width});" - } - - private fun generateReactionCtor(reaction: Reaction) = - """| - |${if (reaction.tardy != null) "LF_DEFINE_REACTION_STP_VIOLATION_HANDLER(${reactor.codeType}, ${reaction.codeName});" else ""} - |${if (reaction.deadline != null) "LF_DEFINE_REACTION_DEADLINE_VIOLATION_HANDLER(${reactor.codeType}, ${reaction.codeName});" else ""} - |LF_DEFINE_REACTION_CTOR(${reactor.codeType}, ${reaction.codeName}, ${reaction.index}, ${reaction.ctorDeadlineArgs}, ${reaction.ctorStpArgs}); - """ - .trimMargin() - - private fun registerObserver(varRef: VarRef, reaction: Reaction) = - when (val variable = varRef.variable) { - is Action -> - "LF_ACTION_REGISTER_OBSERVER(self->${varRef.name}, self->${reaction.codeName});" - is Port -> registerPortObserver(varRef, variable, reaction) - else -> throw AssertionError("Unexpected variable type") - } - - private fun generateSelfStruct(reaction: Reaction) = - "LF_DEFINE_REACTION_STRUCT(${reactor.codeType}, ${reaction.codeName}, ${reaction.totalNumEffects});" - - fun generateSelfStructs() = - reactor.allReactions.joinToString( - separator = "\n", prefix = "// Reaction structs\n", postfix = "\n") { - generateSelfStruct(it) - } - - fun generateReactorStructFields() = - reactor.allReactions.joinToString(separator = "\n", postfix = "\n") { - "LF_REACTION_INSTANCE(${reactor.codeType}, ${it.codeName});" - } - - fun generateReactionCtors() = - reactor.allReactions.joinToString( - separator = "\n", prefix = "// Reaction constructors\n", postfix = "\n") { - generateReactionCtor(it) - } - - fun generateReactionDeadlineViolationHandlers() = - reactionsWithDeadline.joinToString( - separator = "\n", prefix = "// Reaction deadline violation handlers\n", postfix = "\n") { - generateReactionDeadlineViolationHandler(it) - } - - fun generateReactionStpViolationHandlers() = - reactionsWithTardyViolationHandler.joinToString( - separator = "\n", prefix = "// Reaction STP violation handlers\n", postfix = "\n") { - generateReactionStpViolationHandler(it) - } - - fun generateReactionBodies() = - reactor.allReactions.joinToString( - separator = "\n", prefix = "// Reaction bodies\n", postfix = "\n") { - generateReactionBody(it) - } - - private fun generateReactionScope(reaction: Reaction) = - with(PrependOperator) { - """ |// Bring self struct, environment, triggers, effects and sources into scope. - | LF_SCOPE_SELF(${reactor.codeType}); - | LF_SCOPE_ENV(); - ${"| "..generateTriggersEffectsAndSourcesInScope(reaction)} - ${"| "..generateContainedTriggersAndSourcesInScope(reaction)} - """ - .trimMargin() - } - - private fun generateReactionDeadlineViolationHandler(reaction: Reaction) = - with(PrependOperator) { - """ - |LF_DEFINE_REACTION_DEADLINE_VIOLATION_HANDLER(${reactor.codeType}, ${reaction.codeName}) { - ${"| "..generateReactionScope(reaction)} - | // Start of user-witten reaction deadline handler body - ${"| "..reaction.deadline.code.toText()} - |} - """ - .trimMargin() - } - - private fun generateReactionStpViolationHandler(reaction: Reaction) = - with(PrependOperator) { - """ - |LF_DEFINE_REACTION_STP_VIOLATION_HANDLER(${reactor.codeType}, ${reaction.codeName}) { - ${"| "..generateReactionScope(reaction)} - | // Start of user-witten reaction STAA violation handler body - ${"| "..reaction.tardy.code.toText()} - |} - """ - .trimMargin() - } - - private fun generateReactionBody(reaction: Reaction) = - with(PrependOperator) { - """ - |LF_DEFINE_REACTION_BODY(${reactor.codeType}, ${reaction.codeName}) { - ${"| "..generateReactionScope(reaction)} - | // Start of user-witten reaction body - ${"| "..reaction.code.toText()} - |} - """ - .trimMargin() - } - - private fun generateContainedTriggerInScope(trigger: VarRef) = - if (trigger.variable.isMultiport) { - "LF_MULTIPORT_PTR_INSTANCE(${trigger.container.reactor.codeType}, ${trigger.name}, ${(trigger.variable as Port).width});" - } else { - "LF_PORT_PTR_INSTANCE(${trigger.container.reactor.codeType}, ${trigger.name});" - } - - private fun generateContainedMultiportTriggerFieldInit( - instName: String, - containerName: String, - trigger: VarRef, - port: Port - ) = - """| - |${instName}.${trigger.name}_width = ${port.width}; - |for (int j = 0; j<${port.width}; j++) { - | ${instName}.${trigger.name}[j] = ${containerName}.${trigger.name}[j]; - |} - """ - .trimMargin() - - private fun generateContainedTriggerFieldInit(instName: String, trigger: VarRef) = - if (trigger.variable.isMultiport) { - generateContainedMultiportTriggerFieldInit( - instName, "&self->${trigger.container.name}[0]", trigger, trigger.variable as Port) - } else { - "${instName}.${trigger.name} = self->${trigger.container.name}->${trigger.name};" - } - - private fun generateContainedBankTriggerFieldInit(instName: String, trigger: VarRef) = - if (trigger.variable.isMultiport) { - generateContainedMultiportTriggerFieldInit( - "${instName}[i]", - "&self->${trigger.container.name}[i]", - trigger, - trigger.variable as Port) - } else { - "${instName}[i].${trigger.name} = self->${trigger.container.name}[i].${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(separator = "\n") { generateContainedTriggerInScope(it) }} - |}; - |struct _${inst.reactor.codeType}_${inst.name} ${inst.name}; - ${"|"..triggers.joinToString(separator = "\n") {generateContainedTriggerFieldInit("${inst.name}", it)}} - """ - .trimMargin() - } - - private fun generateContainedBankScope(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(separator = "\n") { generateContainedTriggerInScope(it) }} - |}; - |struct _${inst.reactor.codeType}_${inst.name} ${inst.name}[${inst.codeWidth}]; - |size_t ${inst.name}_width = ${inst.codeWidth}; - |for (int i = 0; i<${inst.codeWidth}; i++) { - ${"| "..triggers.joinToString(separator = "\n") {generateContainedBankTriggerFieldInit( "${inst.name}", it)}} - |} - """ - .trimMargin() - } - - private fun generateTriggersEffectsAndSourcesInScope(reaction: Reaction) = - reaction.allUncontainedTriggers - .plus(reaction.allUncontainedEffects) - .plus(reaction.allUncontainedSources) - .joinToString(separator = "\n") { it.scope.toString() } - - private fun generateContainedTriggersAndSourcesInScope(reaction: Reaction) = - reaction.allContainedEffectsTriggersAndSources.toList().joinToString(separator = "\n") { - if (it.first.codeWidth > 1) { - generateContainedBankScope(it.second, it.first) - } else { - generateContainedReactorScope(it.second, it.first) - } - } - - private fun generateTriggerRegisterEffect(reaction: Reaction) = - reaction.triggers.joinToString( - separator = "\n", - ) { - registerEffect(it, reaction) - } - - private fun generateTriggerRegisterObserver(reaction: Reaction) = - reaction.sources.joinToString( - separator = "\n", - ) { - registerObserver(it, reaction) - } - - private fun generateTriggerRegisterSource(reaction: Reaction) = - reaction.effects.joinToString( - separator = "\n", - ) { - registerSource(it, reaction) - } - - private fun generateReactorCtorCode(reaction: Reaction) = - with(PrependOperator) { - """ - |LF_INITIALIZE_REACTION(${reactor.codeType}, ${reaction.codeName}, ${reaction.deadline?.delay.orNever().toCCode()}); - ${" | "..generateTriggerRegisterEffect(reaction)} - ${" | "..generateTriggerRegisterSource(reaction)} - ${" | "..generateTriggerRegisterObserver(reaction)} - """ - .trimMargin() - } - - fun generateReactorCtorCodes() = - reactor.allReactions.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.allReactions) { - if (reaction.allContainedTriggers.any { - it is VarRef && it.container == inst && it.variable == port - }) { - res.add(reaction) - } - } - return res - } - - fun getParentReactionObserversOfOutput(inst: Instantiation, port: Output): List { - val res = mutableListOf() - for (reaction in reactor.allReactions) { - if (reaction.allContainedSources.any { - it is VarRef && it.container == inst && it.variable == port - }) { - res.add(reaction) - } - } - return res - } - - fun getParentReactionSourcesOfInput(inst: Instantiation, port: Input): List { - val res = mutableListOf() - for (reaction in reactor.allReactions) { - if (reaction.allContainedEffects.any { - it is VarRef && it.container == inst && it.variable == port - }) { - res.add(reaction) - } - } - return res - } -} 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 deleted file mode 100644 index f27630394..000000000 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStateGenerator.kt +++ /dev/null @@ -1,33 +0,0 @@ -package org.lflang.generator.uc - -import org.lflang.allStateVars -import org.lflang.generator.uc.UcPortGenerator.Companion.arrayLength -import org.lflang.generator.uc.UcPortGenerator.Companion.isArray -import org.lflang.isInitialized -import org.lflang.lf.Reactor -import org.lflang.toText - -class UcStateGenerator(private val reactor: Reactor) { - fun generateReactorStructFields() = - reactor.allStateVars.joinToString(prefix = "// State variables \n", separator = "\n") { - if (it.type.isArray) { - "${it.type.id} ${it.name}[${it.type.arrayLength}];" - } else { - "${it.type.toText()} ${it.name};" - } - } - - fun generateInitializeStateVars() = - reactor.allStateVars - .filter { it.isInitialized } - .joinToString(prefix = "// Initialize State variables \n", separator = "\n") { - if (it.type.isArray) { - """|${it.type.id} _${it.name}_init[${it.type.arrayLength}] = ${it.init.expr.toCCode()}; - |memcpy(&self->${it.name}, &_${it.name}_init, sizeof(_${it.name}_init)); - """ - .trimMargin() - } else { - "self->${it.name} = ${it.init.expr.toCCode()};" - } - } -} 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 deleted file mode 100644 index 480e25335..000000000 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcTimerGenerator.kt +++ /dev/null @@ -1,42 +0,0 @@ -package org.lflang.generator.uc - -import org.lflang.allTimers -import org.lflang.generator.orNever -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 - -class UcTimerGenerator(private val reactor: Reactor) { - private fun generateSelfStructs(timer: Timer) = - "LF_DEFINE_TIMER_STRUCT(${reactor.codeType}, ${timer.name}, ${reactor.getEffects(timer).size}, ${reactor.getObservers(timer).size});" - - private fun generateCtor(timer: Timer) = - "LF_DEFINE_TIMER_CTOR(${reactor.codeType}, ${timer.name}, ${reactor.getEffects(timer).size}, ${reactor.getObservers(timer).size});" - - fun generateCtors() = - reactor.allTimers.joinToString( - separator = "\n", prefix = "// Timer constructors \n", postfix = "\n") { - generateCtor(it) - } - - fun generateSelfStructs() = - reactor.allTimers.joinToString( - separator = "\n", prefix = "// Timer structs \n", postfix = "\n") { - generateSelfStructs(it) - } - - fun generateReactorStructFields() = - reactor.allTimers.joinToString(separator = "\n", prefix = "// Timers\n", postfix = "\n") { - "LF_TIMER_INSTANCE(${reactor.codeType}, ${it.name});" - } - - private fun generateReactorCtorCode(timer: Timer) = - "LF_INITIALIZE_TIMER(${reactor.codeType}, ${timer.name}, ${timer.offset.toCCode()}, ${timer.period.orNever().toCCode()});" - - fun generateReactorCtorCodes() = - reactor.allTimers.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 deleted file mode 100644 index 8b7d33243..000000000 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcTypes.kt +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2022, 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.TimeUnit -import org.lflang.TimeValue -import org.lflang.generator.TargetTypes - -object UcTypes : TargetTypes { - - override fun supportsGenerics() = true - - override fun getTargetTimeType() = "interval_t" - - override fun getTargetTagType() = "tag_t" - - override fun getTargetUndefinedType() = "void" - - override fun getTargetTimeExpr(timeValue: TimeValue): String = - with(timeValue) { if (magnitude == 0L) "0" else "${unit.cUnit}(${magnitude.toString()})" } -} - -val TimeUnit?.cUnit - get() = - when (this) { - TimeUnit.NANO -> "NSEC" - TimeUnit.MICRO -> "USEC" - TimeUnit.MILLI -> "MSEC" - TimeUnit.SECOND -> "SEC" - TimeUnit.MINUTE -> "MIN" - TimeUnit.HOUR -> "HOUR" - TimeUnit.DAY -> "DAY" - TimeUnit.WEEK -> "WEEK" - else -> "" - } 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 deleted file mode 100644 index 606149612..000000000 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcValidator.kt +++ /dev/null @@ -1,48 +0,0 @@ -package org.lflang.generator.uc - -import java.nio.file.Path -import org.lflang.MessageReporter -import org.lflang.generator.CodeMap -import org.lflang.generator.DiagnosticReporting -import org.lflang.generator.ValidationStrategy -import org.lflang.generator.Validator -import org.lflang.util.LFCommand - -class UcValidator( - private val fileConfig: UcFileConfig, - messageReporter: MessageReporter, - codeMaps: Map -) : Validator(messageReporter, codeMaps) { - - private val ucValidationStrategy = - object : ValidationStrategy { - override fun getCommand(generatedFile: Path?): LFCommand { - return LFCommand.get( - "cargo", - listOf("clippy", "--message-format", "json-diagnostic-rendered-ansi"), - true, - fileConfig.srcGenPkgPath) - } - - override fun getErrorReportingStrategy() = DiagnosticReporting.Strategy { _, _, _ -> } - - override fun getOutputReportingStrategy() = - DiagnosticReporting.Strategy { validationOutput, errorReporter, map -> - for (messageLine in validationOutput.lines()) { - println(messageLine) - } - } - - override fun getPriority(): Int = 0 - - override fun isFullBatch(): Boolean = true - } - - override fun getPossibleStrategies(): Collection = - listOf(ucValidationStrategy) - - override fun getBuildReportingStrategies(): - Pair = - Pair( - ucValidationStrategy.errorReportingStrategy, ucValidationStrategy.outputReportingStrategy) -} diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc2/UcActionGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc2/UcActionGenerator.kt new file mode 100644 index 000000000..fe38ccfb0 --- /dev/null +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc2/UcActionGenerator.kt @@ -0,0 +1,87 @@ +package org.lflang.generator.uc2 + +import org.lflang.generator.PrependOperator +import org.lflang.ir.Action +import org.lflang.ir.Reactor +import org.lflang.ir.Shutdown +import org.lflang.ir.Startup + +class UcActionGenerator(private val reactor: Reactor) { + private fun generateSelfStruct(action: Action): String { + return if (action.type.isVoid) { + "LF_DEFINE_ACTION_STRUCT_VOID(${reactor.codeType}, ${action.lfName}, ${action.actionType}, ${action.getEffects().size}, ${action.getSources().size}, ${action.getObservers().size}, ${action.maxNumPendingEvents});" + } else if (action.type.isArray) { + "LF_DEFINE_ACTION_STRUCT_ARRAY(${reactor.codeType}, ${action.lfName}, ${action.actionType}, ${action.getEffects().size}, ${action.getSources().size}, ${action.getObservers().size}, ${action.maxNumPendingEvents}, ${action.type.targetCode}, ${action.type.arrayLength});" + } else { + "LF_DEFINE_ACTION_STRUCT(${reactor.codeType}, ${action.lfName}, ${action.actionType}, ${action.getEffects().size}, ${action.getSources().size}, ${action.getObservers().size}, ${action.maxNumPendingEvents}, ${action.type.targetCode});" + } + } + + private fun generateCtor(action: Action) = + with(PrependOperator) { + """ + |LF_DEFINE_ACTION_CTOR${if (action.type.isVoid) "_VOID" else ""}(${reactor.codeType}, ${action.lfName}, ${action.actionType}, ${action.getEffects().size}, ${action.getSources().size}, ${action.getObservers().size}, ${action.maxNumPendingEvents} ${if (action.type.isVoid) ", ${action.type.targetCode}" else ""}); + | + """ + .trimMargin() + } + + fun generateCtors(): String = reactor.actions.joinToString(separator = "\n") { generateCtor(it) } + + fun generateSelfStructs(): String = + reactor.actions.joinToString(separator = "\n") { generateSelfStruct(it) } + + fun generateReactorStructFields(): String = + reactor.actions.joinToString( + prefix = "// Actions and builtin triggers\n", separator = "\n", postfix = "\n") { + "LF_ACTION_INSTANCE(${reactor.codeType}, ${it.lfName});" + } + + private fun generateReactorCtorCode(action: Action) = + "LF_INITIALIZE_ACTION(${reactor.codeType}, ${action.lfName}, ${action.minDelay.toCCode()}, ${action.minSpacing.toCCode()});" + + fun generateReactorCtorCodes(): String = + reactor.actions.joinToString( + prefix = "// Initialize actions and builtin triggers\n", + separator = "\n", + postfix = "\n") { + generateReactorCtorCode(it) + } +} + +class BuiltInGenerator(private val reactor: Reactor) { + private fun generateShutdownCtor() = "LF_DEFINE_SHUTDOWN_CTOR(${reactor.codeType});\n" + + private fun generateStartUpCtor() = "LF_DEFINE_STARTUP_CTOR(${reactor.codeType});\n" + + private fun generateShutdownSelfStruct(trigger: Shutdown) = + "LF_DEFINE_SHUTDOWN_STRUCT(${reactor.codeType}, ${trigger.getEffects().size}, ${trigger.getObservers().size});\n" + + private fun generateStartupSelfStruct(trigger: Startup) = + "LF_DEFINE_STARTUP_STRUCT(${reactor.codeType}, ${trigger.getEffects().size}, ${trigger.getObservers().size});\n" + + private fun generateReactorCtorCodeStartup() = "LF_INITIALIZE_STARTUP(${reactor.codeType});\n" + + private fun generateReactorCtorCodeShutdown() = "LF_INITIALIZE_SHUTDOWN(${reactor.codeType});\n" + + fun generateCtors(): String { + var code = String() + if (reactor.hasStartup) code += generateStartUpCtor() + if (reactor.hasShutdown) code += generateShutdownCtor() + return code + } + + fun generateSelfStructs(): String { + var code = String() + if (reactor.hasStartup) code += generateStartupSelfStruct(reactor.startup!!) + if (reactor.hasShutdown) code += generateShutdownSelfStruct(reactor.shutdown!!) + return code + } + + fun generateReactorCtorCodes(): String { + var code = String() + if (reactor.hasStartup) code += generateReactorCtorCodeStartup() + if (reactor.hasShutdown) code += generateReactorCtorCodeShutdown() + return code + } +} diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc2/UcFileConfig.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc2/UcFileConfig.kt new file mode 100644 index 000000000..f2015012a --- /dev/null +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc2/UcFileConfig.kt @@ -0,0 +1,39 @@ +package org.lflang.generator.uc2 + +import org.eclipse.emf.ecore.resource.Resource +import org.lflang.FileConfig +import java.nio.file.Path +import org.lflang.ir.File +import org.lflang.ir.Reactor +import kotlin.io.path.appendText + +class UcFileConfig( + resource: Resource, + srcGenBasePath: Path, + useHierarchicalBin: Boolean, + runtimeSymlink: Boolean +) : FileConfig(resource, srcGenBasePath, useHierarchicalBin, runtimeSymlink) { + + val basePath get(): Path = srcGenBasePath.parent + + //val srcGenBasePath get() : Path = basePath.resolve("src-gen") + + fun relativeToBasePath(path: Path): Path = path.subtract(basePath).first() + + fun generativePath(path: Path): Path { + basePath.appendText("src-gen") + basePath.appendText(relativeToBasePath(path).toString()) + return basePath + } + + private fun getGenDir(path: Path): Path { + return generativePath(path) + } + + /** Path to the header file corresponding to this reactor */ + fun getReactorHeaderPath(r: Reactor): Path = getGenDir(r.location.file).resolve("${r.lfName}.h") + + fun getReactorSourcePath(r: Reactor): Path = getGenDir(r.location.file).resolve("${r.lfName}.c") + + fun getPreambleHeaderPath(r: File): Path = getGenDir(r.path).resolve("_lf_preamble.h") +} diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc2/UcGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc2/UcGenerator.kt new file mode 100644 index 000000000..fb22f045a --- /dev/null +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc2/UcGenerator.kt @@ -0,0 +1,100 @@ +package org.lflang.generator.uc2 + +import java.nio.file.Path +import org.apache.commons.lang3.tuple.MutablePair +import org.lflang.MessageReporter +import org.lflang.generator.GeneratorBase +import org.lflang.generator.LFGeneratorContext +import org.lflang.ir.Environment +import org.lflang.ir.Federate +import org.lflang.ir.File +import org.lflang.ir.ReactorInstantiation + +import org.lflang.ir.Reactor +import org.lflang.scoping.LFGlobalScopeProvider + +/** Creates either a Federated or NonFederated generator depending on the type of LF program */ +public fun createUcGenerator( + context: LFGeneratorContext, + scopeProvider: LFGlobalScopeProvider, + env: Environment, + mainReactor: Reactor, + messageReporter: MessageReporter, +): UcGenerator { + return UcGeneratorNonFederated(env, mainReactor, messageReporter, context, scopeProvider) +} + +@Suppress("unused") +abstract class UcGenerator( + val context: LFGeneratorContext, + protected val scopeProvider: LFGlobalScopeProvider +) : GeneratorBase(context) { + + // keep a list of all source files we generate + val ucSources = mutableListOf() + val fileConfig: UcFileConfig = context.fileConfig as UcFileConfig + + // Contains the maximum number of pending events required by each reactor. + // Is updated as reactors are analyzed and code-generated. + val maxNumPendingEvents = mutableMapOf() + + // Compute the total number of events and reactions within an instance (and its children) + // Also returns whether there is any startup event within the instance. + private fun totalNumEventsAndReactions(inst: ReactorInstantiation): Triple { + var numEvents = 0 + var numReactions = 0 + var hasStartup = false + val remaining = mutableListOf() + remaining.addAll(inst.reactor.childReactors) + while (remaining.isNotEmpty()) { + val child = remaining.removeFirst() + val childRes = totalNumEventsAndReactions(child) + + numEvents += childRes.first * child.codeWidth + numReactions += childRes.second * child.codeWidth + hasStartup = hasStartup or childRes.third + } + numEvents += maxNumPendingEvents[inst.reactor]!! + numReactions += inst.reactor.reactions.size + hasStartup = hasStartup or inst.reactor.hasStartup + return Triple(numEvents, numReactions, hasStartup) + } + + // Compute the total number of events and reactions for a top-level reactor. + fun totalNumEventsAndReactions(main: Reactor): Pair { + val res = MutablePair(maxNumPendingEvents[main]!!, main.reactions.size) + var hasStartup = main.hasStartup + for (inst in main.childReactors) { + val childRes = totalNumEventsAndReactions(inst) + res.left += childRes.first * inst.codeWidth + res.right += childRes.second * inst.codeWidth + hasStartup = hasStartup or childRes.third + } + if (hasStartup) res.left += 1 + return res.toPair() + } + + companion object { + const val libDir = "/lib/c" + const val MINIMUM_CMAKE_VERSION = "3.5" + } + + // Returns a list of all instantiated reactors within a top-level reactor. + protected fun getAllInstantiatedReactors(top: Reactor): List { + val res = mutableListOf() + for (inst in top.childReactors) { + res.add(inst.reactor) + res.addAll(getAllInstantiatedReactors(inst.reactor)) + } + return res.distinct() + } + + protected fun getAllImportedResources(resource: File, resources: MutableSet) { + resource.imports.forEach { + if (!resources.contains(it.file)) { + resources.add(it.file) + getAllImportedResources(it.file, resources) + } + } + } +} diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc2/UcGeneratorNonFederated.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc2/UcGeneratorNonFederated.kt new file mode 100644 index 000000000..3bd93e39f --- /dev/null +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc2/UcGeneratorNonFederated.kt @@ -0,0 +1,107 @@ +package org.lflang.generator.uc2 + +import org.eclipse.emf.ecore.resource.Resource +import org.lflang.MessageReporter +import org.lflang.generator.GeneratorResult +import org.lflang.generator.LFGeneratorContext +import org.lflang.generator.TargetTypes +import org.lflang.ir.Environment +import org.lflang.ir.Federate +import org.lflang.ir.File +import org.lflang.ir.Reactor +import org.lflang.scoping.LFGlobalScopeProvider +import org.lflang.target.Target +import org.lflang.util.FileUtil +import java.nio.file.Path + + +class UcGeneratorNonFederated( + val env: Environment, + val mainReactor: Reactor, + val messageReporter: MessageReporter, + context: LFGeneratorContext, + scopeProvider: LFGlobalScopeProvider +) : UcGenerator(context, scopeProvider) { + lateinit var file: File + + override fun doGenerate(resource: Resource, context: LFGeneratorContext) { + println("UCGenerator !!!"); + val res = + doGenerateReactor( + file, + context, + fileConfig.srcGenBasePath, + ) + + if (res == GeneratorResult.Status.GENERATED) { + // generate platform specific files + //TODO: val platformGenerator = UcPlatformGeneratorNonFederated(this, srcGenPath,) + // platformGenerator.generatePlatformFiles() + + // TODO: + // platformGenerator.doCompile(context) + // context.finish(GeneratorResult.Status.COMPILED, codeMaps) + + //return GeneratorResult.GENERATED_NO_EXECUTABLE as GeneratorResult + } else { + //return GeneratorResult.FAILED + } + } + + fun doGenerateReactor( + file: File, + context: LFGeneratorContext, + srcGenPath: Path, + ): GeneratorResult.Status { + if (context.args.generateFedTemplates) { + messageReporter + .nowhere() + .error("Cannot generate federate templates for non-federated program") + return GeneratorResult.Status.FAILED + } + + // Generate header and source files for all instantiated reactors. + getAllInstantiatedReactors(mainReactor).map { generateReactorFiles(it, srcGenPath) } + + // Generate header and source files for the main reactor. + generateReactorFiles(mainReactor, srcGenPath) + + // Generate preambles for all reactors. + generateAllPreambles(file, srcGenPath) + + return GeneratorResult.Status.GENERATED + } + + fun generateAllPreambles(file: File, srcGenPath: Path) { + var files: MutableSet = mutableSetOf(); + getAllImportedResources(file, files) + for (file in files) { + for (reactor in file.reactors) { + val generator = UcPreambleGenerator(file, fileConfig) + val preambleHeaderPath = fileConfig.getPreambleHeaderPath(file) + FileUtil.writeToFile(generator.generateHeader(), srcGenPath.resolve(preambleHeaderPath), true) + } + } + } + + fun generateReactorFiles(reactor: Reactor, srcGenPath: Path) { + val generator = UcReactorGenerator(reactor, fileConfig) + + // paths to source files + val headerFile = fileConfig.getReactorHeaderPath(reactor) + val sourceFile = fileConfig.getReactorSourcePath(reactor) + ucSources.add(sourceFile) + + // write to file + FileUtil.writeToFile(generator.generateSource(), srcGenPath.resolve(headerFile), true) + FileUtil.writeToFile(generator.generateHeader(), srcGenPath.resolve(sourceFile), true) + } + + override fun getTargetTypes(): TargetTypes? { + TODO("Not yet implemented") + } + + override fun getTarget(): Target? { + TODO("Not yet implemented") + } +} diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcInstanceGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc2/UcInstanceGenerator.kt similarity index 51% rename from lfc/core/src/main/kotlin/org/lflang/generator/uc/UcInstanceGenerator.kt rename to lfc/core/src/main/kotlin/org/lflang/generator/uc2/UcInstanceGenerator.kt index 03cd2896f..1ecb5937a 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcInstanceGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc2/UcInstanceGenerator.kt @@ -1,65 +1,49 @@ -package org.lflang.generator.uc +package org.lflang.generator.uc2 -import org.lflang.* import org.lflang.generator.PrependOperator -import org.lflang.generator.uc.UcPortGenerator.Companion.width -import org.lflang.generator.uc.UcReactorGenerator.Companion.codeType -import org.lflang.lf.* +import org.lflang.ir.Reactor +import org.lflang.ir.ReactorInstantiation +import org.lflang.toUnixString class UcInstanceGenerator( private val reactor: Reactor, private val parameters: UcParameterGenerator, private val ports: UcPortGenerator, - private val connections: UcConnectionGenerator, + private val connections: UcLocalConnectionGenerator, private val reactions: UcReactionGenerator, private val fileConfig: UcFileConfig, - private val messageReporter: MessageReporter ) { - companion object { - val Instantiation.width - get(): Int = widthSpec?.getWidth() ?: 1 - - val Instantiation.codeWidth - get(): Int = if (this.isAFederate) 1 else width - - val Instantiation.codeTypeFederate - get(): String = "${(eContainer() as Reactor).name}_${name}" - - val Instantiation.isAFederate - get(): Boolean = this.eContainer() is Reactor && (this.eContainer() as Reactor).isFederated - } - fun generateIncludes(): String = - reactor.allInstantiations + reactor.childReactors .map { fileConfig.getReactorHeaderPath(it.reactor) } .distinct() .joinToString(prefix = "// Include instantiated reactors\n", separator = "\n") { """#include "${it.toUnixString()}" """ } - fun generateReactorStructContainedOutputFields(inst: Instantiation) = - inst.reactor.allOutputs.joinToString(separator = "\n") { + fun generateReactorStructContainedOutputFields(inst: ReactorInstantiation) = + inst.reactor.outputs.joinToString(separator = "\n") { with(PrependOperator) { """| - |LF_CHILD_OUTPUT_CONNECTIONS(${inst.name}, ${it.name}, ${inst.codeWidth}, ${it.width}, ${connections.getNumConnectionsFromPort(inst, it)}); - |LF_CHILD_OUTPUT_EFFECTS(${inst.name}, ${it.name}, ${inst.codeWidth}, ${it.width}, ${reactions.getParentReactionEffectsOfOutput(inst, it).size}); - |LF_CHILD_OUTPUT_OBSERVERS(${inst.name}, ${it.name}, ${inst.codeWidth}, ${it.width}, ${reactions.getParentReactionObserversOfOutput(inst, it).size}); + |LF_CHILD_OUTPUT_CONNECTIONS(${inst.name}, ${it.lfName}, ${inst.codeWidth}, ${it.width}, ${connections.getNumConnectionsFromPort(inst, it)}); + |LF_CHILD_OUTPUT_EFFECTS(${inst.name}, ${it.lfName}, ${inst.codeWidth}, ${it.width}, ${reactions.getParentReactionEffectsOfOutput(inst, it).size}); + |LF_CHILD_OUTPUT_OBSERVERS(${inst.name}, ${it.lfName}, ${inst.codeWidth}, ${it.width}, ${reactions.getParentReactionObserversOfOutput(inst, it).size}); """ .trimMargin() } } - fun generateReactorStructContainedInputFields(inst: Instantiation) = - inst.reactor.allInputs.joinToString(separator = "\n") { + fun generateReactorStructContainedInputFields(inst: ReactorInstantiation) = + inst.reactor.inputs.joinToString(separator = "\n") { with(PrependOperator) { """| - |LF_CHILD_INPUT_SOURCES(${inst.name}, ${it.name}, ${inst.codeWidth}, ${it.width}, ${reactions.getParentReactionSourcesOfInput(inst, it).size}); + |LF_CHILD_INPUT_SOURCES(${inst.name}, ${it.lfName}, ${inst.codeWidth}, ${it.width}, ${reactions.getParentReactionSourcesOfInput(inst, it).size}); """ .trimMargin() } } - fun generateReactorStructField(inst: Instantiation) = + fun generateReactorStructField(inst: ReactorInstantiation) = with(PrependOperator) { """| |LF_CHILD_REACTOR_INSTANCE(${inst.reactor.codeType}, ${inst.name}, ${inst.codeWidth}); @@ -70,12 +54,12 @@ class UcInstanceGenerator( } fun generateReactorStructFields() = - reactor.allInstantiations.joinToString( + reactor.childReactors.joinToString( prefix = "// Child reactor fields\n", separator = "\n", postfix = "\n") { generateReactorStructField(it) } - fun generateReactorCtorCode(inst: Instantiation) = + fun generateReactorCtorCode(inst: ReactorInstantiation) = with(PrependOperator) { """| ${" |"..ports.generateDefineContainedOutputArgs(inst)} @@ -89,5 +73,5 @@ class UcInstanceGenerator( } fun generateReactorCtorCodes() = - reactor.allInstantiations.joinToString(separator = "\n") { generateReactorCtorCode(it) } + reactor.childReactors.joinToString(separator = "\n") { generateReactorCtorCode(it) } } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc2/UcLocalConnectionGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc2/UcLocalConnectionGenerator.kt new file mode 100644 index 000000000..e85a5bf64 --- /dev/null +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc2/UcLocalConnectionGenerator.kt @@ -0,0 +1,128 @@ +package org.lflang.generator.uc2 + +import org.lflang.ir.Connection +import org.lflang.ir.ConnectionKind +import org.lflang.ir.Port +import org.lflang.ir.ReactorInstantiation +import org.lflang.ir.Reactor +import org.lflang.ir.TimeValue +import org.lflang.ir.VariableContainedTriggerRef +import org.lflang.ir.VariableNameTriggerRef +import kotlin.collections.mutableListOf + +class ConnectionCharacteristics( + val src: Port, + val delay: TimeValue, + val isPhysical: Boolean, + val kind: ConnectionKind, +) + +class UcLocalConnectionGenerator(val reactor: Reactor) { + + val groupedConnections = generateGroupConnections(reactor.connections) + + val Connection.getUniqueName get() : String = when (sourceRef) { + is VariableContainedTriggerRef -> "conn_${(this as VariableNameTriggerRef).container.instantiation.name} _${(this as VariableNameTriggerRef).name}_${uid}" + is VariableNameTriggerRef -> "conn__${(this as VariableNameTriggerRef).name}_${uid}" + else -> throw IllegalStateException("unreachable") + } + + fun generateGroupConnections(connections: List): List { + var res = mutableMapOf>() + + for (connection in connections) { + val characteristics = ConnectionCharacteristics( + src = connection.source, + delay = connection.delay, + isPhysical = connection.isPhysical, + kind = connection.kind, + ); + res.getOrPut(characteristics) { mutableListOf() }.also { res[characteristics]?.plus(connection) } + } + + var results = mutableListOf() + for (pair in res) { + val targets = pair.value + .fold(mutableListOf()) { acc, it -> + acc.plus(it.targets) + acc.toMutableList() + }; + + + results.plus(Connection( + sourceRef = pair.key.src.triggerRef(pair.value[0].container), + targetPortRefs = targets.map { it.triggerRef(it.container) }, + kind = pair.key.kind, + delay = pair.key.delay, + bufferSize = 10, + width = 1, + isIterated = false + )) + } + + return results + } + + fun getNumConnectionsFromPort(instantiation: ReactorInstantiation?, port: Port): Int { + var count = groupedConnections.first { it.source == port && it.container == instantiation?.reactor }.targetPortRefs.size + return count + } + + private fun generateLogicalSelfStruct(conn: Connection) = + "LF_DEFINE_LOGICAL_CONNECTION_STRUCT(${reactor.codeType}, ${conn.getUniqueName}, ${conn.numDownstreams});" + + private fun generateLogicalCtor(conn: Connection) = + "LF_DEFINE_LOGICAL_CONNECTION_CTOR(${reactor.codeType}, ${conn.getUniqueName}, ${conn.numDownstreams});" + + private fun generateDelayedSelfStruct(conn: Connection) = + if (conn.source.dataType.isArray) + "LF_DEFINE_DELAYED_CONNECTION_STRUCT_ARRAY(${reactor.codeType}, ${conn.getUniqueName}, ${conn.numDownstreams}, ${conn.source.dataType}, ${conn.maxNumPendingEvents}, ${conn.source.dataType.arrayLength});" + else + "LF_DEFINE_DELAYED_CONNECTION_STRUCT(${reactor.codeType}, ${conn.getUniqueName}, ${conn.numDownstreams}, ${conn.source.dataType}, ${conn.maxNumPendingEvents});" + + private fun generateDelayedCtor(conn: Connection) = + "LF_DEFINE_DELAYED_CONNECTION_CTOR(${reactor.codeType}, ${conn.getUniqueName}, ${conn.numDownstreams}, ${conn.maxNumPendingEvents}, ${conn.isPhysical});" + + + private fun generateReactorCtorCode(conn: Connection) = + if (conn.isLogical) { + "LF_INITIALIZE_LOGICAL_CONNECTION(${reactor.codeType}, ${conn.getUniqueName}, ${conn.width}, ${conn.source.width})" + } else { + "LF_INITIALIZE_DELAYED_CONNECTION(${reactor.codeType}, ${conn.getUniqueName}, ${conn.delay}, ${conn.width}, ${conn.source.width})" + } + + fun generateCtors() = + groupedConnections.joinToString( + prefix = "// Connection constructors\n", separator = "\n", postfix = "\n") { + if (it.isDelayed) generateDelayedCtor(it) else generateLogicalCtor(it) + } + + fun generateSelfStructs() = + groupedConnections.joinToString( + prefix = "// Connection structs\n", separator = "\n", postfix = "\n") { + if (it.isLogical) generateLogicalSelfStruct(it) else generateDelayedSelfStruct(it) + } + + fun generateReactorStructFields() = + groupedConnections.joinToString( + prefix = "// Connections \n", separator = "\n", postfix = "\n") { + if (it.isLogical) + "LF_LOGICAL_CONNECTION_INSTANCE(${reactor.codeType}, ${it.getUniqueName}, ${it.width}, ${it.width});" //TODO: + else + "LF_DELAYED_CONNECTION_INSTANCE(${reactor.codeType}, ${it.getUniqueName}, ${it.width}, ${it.width});" //TODO: + } + + //private fun generateConnectionStatements(conn: Connection) = + // conn.channels.joinToString(separator = "\n") { generateConnectChannel(conn, it) } + + fun generateReactorCtorCodes() = + groupedConnections.joinToString( + prefix = "// Initialize connections\n", separator = "\n", postfix = "\n") { + generateReactorCtorCode(it) + } + // + + // groupedConnections.joinToString( + // prefix = "// Do connections \n", separator = "\n", postfix = "\n") { + // generateConnectionStatements(it) + // } +} \ No newline at end of file diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc2/UcParameterGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc2/UcParameterGenerator.kt new file mode 100644 index 000000000..d78501d67 --- /dev/null +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc2/UcParameterGenerator.kt @@ -0,0 +1,49 @@ +package org.lflang.generator.uc2 + +import org.lflang.ir.Parameter +import org.lflang.ir.Reactor +import org.lflang.ir.ReactorInstantiation + +class UcParameterGenerator(private val reactor: Reactor) { + + companion object { + + val Parameter.targetType + get(): String = this.type.targetCode.code + + val Parameter.isPresentName + get(): String = "__${this.name}" + } + + fun generateReactorStructFields() = + reactor.instantiation.allParameters.joinToString( + prefix = "// Reactor parameters\n", separator = "\n", postfix = "\n") { + "${it.targetType} ${it.name};" + } + + fun generateReactorCtorCodes() = + reactor.instantiation.allParameters.joinToString( + prefix = "// Initialize Reactor parameters\n", separator = "\n", postfix = "\n") { + """| + |self->${it.name} = ${it.name}; + """ + .trimMargin() + } + + fun generateReactorCtorDefArguments() = + reactor.instantiation.allParameters.joinToString(separator = "") { ", ${it.targetType} ${it.name}" } + + fun generateReactorCtorDefaultArguments() = + reactor.instantiation.allParameters.joinToString(separator = "") { ", ${it.init.code}" } + + fun generateReactorCtorDeclArguments(r: ReactorInstantiation) = + r.reactor.instantiation.allParameters.joinToString(separator = "") { + if (it.name == "bank_idx" || it.name == "bank_index") { + ", i" + } else if (r.reactor.instantiation.allParameters.none { p -> p.name == it.name }) { + ", ${it.init.code}" + } else { + ", ${r.allParameters.find{ p -> p.name == it.name}!!.value.code}" + } + } +} diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc2/UcPortGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc2/UcPortGenerator.kt new file mode 100644 index 000000000..e6565cefb --- /dev/null +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc2/UcPortGenerator.kt @@ -0,0 +1,125 @@ +/** + * ********** + * Copyright (c) 2019-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.uc2 + +import org.lflang.ir.InputPort +import org.lflang.ir.OutputPort +import org.lflang.ir.Port +import org.lflang.ir.Reactor +import org.lflang.ir.ReactorInstantiation + +class UcPortGenerator( + private val reactor: Reactor, + private val connections: UcLocalConnectionGenerator +) { + + private fun generateSelfStruct(input: InputPort): String { + if (input.dataType.isArray) { + return "LF_DEFINE_INPUT_ARRAY_STRUCT(${reactor.codeType}, ${input.lfName}, ${input.getEffects().size}, ${input.getObservers().size}, ${input.dataType.targetCode}, ${input.dataType.arrayLength}, ${connections.getNumConnectionsFromPort(null, input as Port)});" + } else { + return "LF_DEFINE_INPUT_STRUCT(${reactor.codeType}, ${input.lfName}, ${input.getEffects().size}, ${input.getObservers().size}, ${input.dataType.targetCode}, ${connections.getNumConnectionsFromPort(null, input as Port)});" + } + } + + private fun generateInputCtor(input: InputPort) = + "LF_DEFINE_INPUT_CTOR(${reactor.codeType}, ${input.lfName}, ${input.getEffects().size}, ${input.getObservers().size}, ${input.dataType.targetCode}, ${connections.getNumConnectionsFromPort(null, input as Port)});" + + private fun generateSelfStruct(output: OutputPort): String { + if (output.dataType.isArray) { + return "LF_DEFINE_OUTPUT_ARRAY_STRUCT(${reactor.codeType}, ${output.lfName}, ${output.getSources().size}, ${output.dataType.targetCode}, ${output.dataType.arrayLength});" + } else { + return "LF_DEFINE_OUTPUT_STRUCT(${reactor.codeType}, ${output.lfName}, ${output.getSources().size}, ${output.dataType.targetCode});" + } + } + + private fun generateOutputCtor(output: OutputPort) = + "LF_DEFINE_OUTPUT_CTOR(${reactor.codeType}, ${output.lfName}, ${output.getSources().size});" + + fun generateSelfStructs() = + reactor.inputs.plus(reactor.outputs).joinToString( + prefix = "// Port structs\n", separator = "\n", postfix = "\n") { + when (it) { + is InputPort -> generateSelfStruct(it) + is OutputPort -> generateSelfStruct(it) + else -> "" + } + } + + fun generateReactorStructFields() = + reactor.inputs.plus(reactor.outputs).joinToString( + prefix = "// Ports \n", separator = "\n", postfix = "\n") { + "LF_PORT_INSTANCE(${reactor.codeType}, ${it.lfName}, ${it.width});" + } + + fun generateCtors() = + reactor.inputs.plus(reactor.outputs).joinToString( + prefix = "// Port constructors\n", separator = "\n", postfix = "\n") { + when (it) { + is InputPort -> generateInputCtor(it) + is OutputPort -> generateOutputCtor(it) + else -> throw IllegalArgumentException("Error: Port was neither input nor output") + } + } + + private fun generateReactorCtorCode(input: InputPort) = + "LF_INITIALIZE_INPUT(${reactor.codeType}, ${input.lfName}, ${input.width}, ${input.externalArgs});" + + private fun generateReactorCtorCode(output: OutputPort) = + "LF_INITIALIZE_OUTPUT(${reactor.codeType}, ${output.lfName}, ${output.width}, ${output.externalArgs});" + + private fun generateReactorCtorCode(port: Port) = + when (port) { + is InputPort -> generateReactorCtorCode(port) + is OutputPort -> generateReactorCtorCode(port) + else -> throw IllegalArgumentException("Error: Port was neither input nor output") + } + + fun generateReactorCtorCodes() = + reactor.inputs.plus(reactor.outputs).joinToString( + prefix = "// Initialize ports\n", separator = "\n", postfix = "\n") { + generateReactorCtorCode(it) + } + + fun generateDefineContainedOutputArgs(r: ReactorInstantiation) = + r.reactor.outputs.joinToString(separator = "\n", prefix = "\n", postfix = "\n") { + "LF_DEFINE_CHILD_OUTPUT_ARGS(${r.name}, ${it.lfName}, ${r.codeWidth}, ${it.width});" + } + + fun generateDefineContainedInputArgs(r: ReactorInstantiation) = + r.reactor.inputs.joinToString(separator = "\n", prefix = "\n", postfix = "\n") { + "LF_DEFINE_CHILD_INPUT_ARGS(${r.name}, ${it.lfName}, ${r.codeWidth}, ${it.width});" + } + + fun generateReactorCtorDefArguments() = + reactor.outputs.joinToString(separator = "") { + ", OutputExternalCtorArgs *${it.externalArgs}" + } + + reactor.inputs.joinToString(separator = "") { + ", InputExternalCtorArgs *${it.externalArgs}" + } + + fun generateReactorCtorDeclArguments(r: ReactorInstantiation) = + r.reactor.outputs.plus(r.reactor.inputs).joinToString(separator = "") { + ", _${r.name}_${it.lfName}_args[i]" + } +} diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPreambleGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc2/UcPreambleGenerator.kt similarity index 61% rename from lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPreambleGenerator.kt rename to lfc/core/src/main/kotlin/org/lflang/generator/uc2/UcPreambleGenerator.kt index a2060cbe4..2d027cb46 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPreambleGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc2/UcPreambleGenerator.kt @@ -1,26 +1,21 @@ -package org.lflang.generator.uc +package org.lflang.generator.uc2 -import org.eclipse.emf.common.util.EList -import org.eclipse.emf.ecore.resource.Resource -import org.lflang.* import org.lflang.generator.PrependOperator -import org.lflang.generator.PrependOperator.rangeTo -import org.lflang.lf.Preamble -import org.lflang.scoping.LFGlobalScopeProvider +import org.lflang.ir.File +import org.lflang.ir.Preamble class UcPreambleGenerator( - private val resource: Resource, + private val resource: File, private val fileConfig: UcFileConfig, - private val scopeProvider: LFGlobalScopeProvider ) { /** A list of all preambles defined in the resource (file) */ - private val preambles: EList = resource.model.preambles + private val preambles: List = resource.preambles private val includeGuard = "LF_GEN_${resource.name.uppercase()}_PREAMBLE_H" fun generateHeader(): String { - val importedResources = scopeProvider.getImportedResources(resource) + val importedResources = resource.imports val includes = - importedResources.map { """#include "${fileConfig.getPreambleHeaderPath(it)}"""" } + importedResources.map { """#include "${fileConfig.getPreambleHeaderPath(it.file)}"""" } return with(PrependOperator) { """ @@ -30,7 +25,7 @@ class UcPreambleGenerator( |#include "reactor-uc/reactor-uc.h" ${" |"..includes.joinToString(separator = "\n", prefix = "// Include the preambles from imported files \n")} | - ${" |"..preambles.joinToString(separator = "\n") { it.code.toText() }} + ${" |"..preambles.joinToString(separator = "\n") { it.code.code }} |#endif // ${includeGuard} """ .trimMargin() diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc2/UcReactionGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc2/UcReactionGenerator.kt new file mode 100644 index 000000000..6c962a320 --- /dev/null +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc2/UcReactionGenerator.kt @@ -0,0 +1,375 @@ +package org.lflang.generator.uc2 + +import org.lflang.generator.PrependOperator +import org.lflang.ir.* + +class UcReactionGenerator(private val reactor: Reactor) { + + private val reactionsWithDeadline = reactor.reactions.filter { it.deadline != null } + private val reactionsWithTardyViolationHandler = + reactor.reactions.filter { it.tardy != null } + + + private val TriggerRef.scope + get() = + when (this.resolve()) { + is Startup -> "LF_SCOPE_STARTUP(${reactor.codeType});" + is Shutdown -> "LF_SCOPE_SHUTDOWN(${reactor.codeType});" + is Timer -> "LF_SCOPE_TIMER(${reactor.codeType}, ${(this.resolve() as Timer).lfName});" + is Action -> "LF_SCOPE_ACTION(${reactor.codeType}, ${(this.resolve() as Action).lfName});" + is Port -> { + if ((this.resolve() as Port).width > 1) { + "LF_SCOPE_MULTIPORT(${reactor.codeType}, ${(this.resolve() as Port).lfName});" + } else { + "LF_SCOPE_PORT(${reactor.codeType}, ${(this.resolve() as Port).lfName});" + } + } + else -> throw AssertionError("Unexpected variable type") + } + + private fun registerPortSource(triggerRef: TriggerRef, port: Port, reaction: Reaction) : String = + when (triggerRef) { + is VariableNameTriggerRef -> { + "LF_PORT_REGISTER_SOURCE(self->${triggerRef.variable.lfName}, ${reaction.nameInReactor}, ${port.width});" + } + is VariableContainedTriggerRef -> { + (0..${triggerRef.container.lfName}[${it}].${port.lfName}, ${reaction.nameInReactor}, ${port.width})" + } + } + else -> throw AssertionError("Unexpected variable type $triggerRef") + } + + private fun registerSource(triggerRef: TriggerRef, reaction: Reaction) : String = + when (val variable = triggerRef.resolve()) { + is Action -> "LF_ACTION_REGISTER_SOURCE(self->${variable.lfName}, ${reaction.nameInReactor});" + is Port -> registerPortSource(triggerRef, variable, reaction) + else -> throw AssertionError("Unexpected variable type $triggerRef") + } + + private fun registerEffect(triggerRef: TriggerRef, reaction: Reaction) : String = + when { + triggerRef is StartupTriggerRef -> "LF_STARTUP_REGISTER_EFFECT(${reaction.nameInReactor});" + triggerRef is ShutdownTriggerRef -> + "LF_SHUTDOWN_REGISTER_EFFECT(${reaction.nameInReactor});" + triggerRef is VariableContainedTriggerRef -> registerEffect(triggerRef, reaction) + triggerRef is VariableNameTriggerRef -> registerEffect(triggerRef, reaction) + else -> throw AssertionError("Unexpected variable type") + } + + private fun registerPortEffect(triggerRef: TriggerRef, port: Port, reaction: Reaction) = + when (triggerRef) { + is VariableNameTriggerRef -> + "LF_PORT_REGISTER_EFFECT(self->${triggerRef.variable.lfName}, ${reaction.nameInReactor}, ${port.width});" + is VariableContainedTriggerRef -> + (0..${triggerRef.container.lfName}[${it}].${port.lfName}, ${reaction.nameInReactor}, ${port.width})" + } + else -> throw AssertionError("Unexpected variable type") + } + + private fun registerPortObserver(triggerRef: TriggerRef, port: Port, reaction: Reaction) = + when (triggerRef) { + is VariableNameTriggerRef -> + "LF_PORT_REGISTER_OBSERVER(self->${triggerRef.resolve().lfName}, ${reaction.nameInReactor}, ${port.width});" + is VariableContainedTriggerRef -> + (0..${triggerRef.container.lfName}[${it}].${port.lfName}, ${reaction.nameInReactor}, ${port.width})" + } + else -> throw AssertionError("Unexpected variable type") + } + + private fun registerEffect(triggerRef: VariableNameTriggerRef, reaction: Reaction) = + when (val variable = triggerRef.variable) { + is Timer -> + "LF_TIMER_REGISTER_EFFECT(self->${triggerRef.variable.lfName}, ${reaction.nameInReactor});" + is Action -> + "LF_ACTION_REGISTER_EFFECT(self->${triggerRef.variable.lfName}, ${reaction.nameInReactor});" + is Port -> registerPortEffect(triggerRef, variable, reaction) + else -> throw AssertionError("Unexpected variable type") + } + + private fun generateReactionCtor(reaction: Reaction) = + """| + |${if (reaction.tardy != null) "LF_DEFINE_REACTION_STP_VIOLATION_HANDLER(${reactor.codeType}, ${reaction.codeName});" else ""} + |${if (reaction.deadline != null) "LF_DEFINE_REACTION_DEADLINE_VIOLATION_HANDLER(${reactor.codeType}, ${reaction.codeName});" else ""} + |LF_DEFINE_REACTION_CTOR(${reactor.codeType}, ${reaction.codeName}, ${reaction.index}, ${reaction.ctorDeadlineArgs}, ${reaction.ctorStpArgs}); + """ + .trimMargin() + + private fun registerObserver(triggerRef: TriggerRef, reaction: Reaction) = + when (val variable = triggerRef.resolve()) { + is Action -> + "LF_ACTION_REGISTER_OBSERVER(self->${variable.lfName}, self->${reaction.codeName});" + is Port -> registerPortObserver(triggerRef, variable, reaction) + else -> throw AssertionError("Unexpected variable type") + } + + private fun generateSelfStruct(reaction: Reaction) = + "LF_DEFINE_REACTION_STRUCT(${reactor.codeType}, ${reaction.codeName}, ${reaction.totalNumEffects});" + + fun generateSelfStructs() = + reactor.reactions.joinToString( + separator = "\n", prefix = "// Reaction structs\n", postfix = "\n") { + generateSelfStruct(it) + } + + fun generateReactorStructFields() = + reactor.reactions.joinToString(separator = "\n", postfix = "\n") { + "LF_REACTION_INSTANCE(${reactor.codeType}, ${it.codeName});" + } + + fun generateReactionCtors() = + reactor.reactions.joinToString( + separator = "\n", prefix = "// Reaction constructors\n", postfix = "\n") { + generateReactionCtor(it) + } + + fun generateReactionDeadlineViolationHandlers() = + reactionsWithDeadline.joinToString( + separator = "\n", prefix = "// Reaction deadline violation handlers\n", postfix = "\n") { + generateReactionDeadlineViolationHandler(it) + } + + fun generateReactionStpViolationHandlers() = + reactionsWithTardyViolationHandler.joinToString( + separator = "\n", prefix = "// Reaction STP violation handlers\n", postfix = "\n") { + generateReactionStpViolationHandler(it) + } + + fun generateReactionBodies() = + reactor.reactions.joinToString( + separator = "\n", prefix = "// Reaction bodies\n", postfix = "\n") { + generateReactionBody(it) + } + + private fun generateReactionScope(reaction: Reaction) = + with(PrependOperator) { + """ |// Bring self struct, environment, triggers, effects and sources into scope. + | LF_SCOPE_SELF(${reactor.codeType}); + | LF_SCOPE_ENV(); + ${"| "..generateTriggersEffectsAndSourcesInScope(reaction)} + ${"| "..generateContainedTriggersAndSourcesInScope(reaction)} + """ + .trimMargin() + } + + private fun generateReactionDeadlineViolationHandler(reaction: Reaction) = + with(PrependOperator) { + """ + |LF_DEFINE_REACTION_DEADLINE_VIOLATION_HANDLER(${reactor.codeType}, ${reaction.codeName}) { + ${"| "..generateReactionScope(reaction)} + | // Start of user-witten reaction deadline handler body + ${"| "..(reaction.deadline?.body?.code ?: "")} + |} + """ + .trimMargin() + } + + private fun generateReactionStpViolationHandler(reaction: Reaction) = + with(PrependOperator) { + """ + |LF_DEFINE_REACTION_STP_VIOLATION_HANDLER(${reactor.codeType}, ${reaction.codeName}) { + ${"| "..generateReactionScope(reaction)} + | // Start of user-witten reaction STAA violation handler body + ${"| "..(reaction.tardy?.body?.code ?: "")} + |} + """ + .trimMargin() + } + + private fun generateReactionBody(reaction: Reaction) = + with(PrependOperator) { + """ + |LF_DEFINE_REACTION_BODY(${reactor.codeType}, ${reaction.codeName}) { + ${"| "..generateReactionScope(reaction)} + | // Start of user-witten reaction body + ${"| "..reaction.body.code} + |} + """ + .trimMargin() + } + + private fun generateContainedTriggerInScope(triggerRef: TriggerRef) = + when (triggerRef.resolve()) { + is Port -> { + var port = triggerRef.resolve() as Port + if (port.isMultiport) { + "LF_MULTIPORT_PTR_INSTANCE(${port.container.codeType}, ${port.lfName}, ${port.width});" + } else { + "LF_PORT_PTR_INSTANCE(${port.container.codeType}, ${port.lfName});" + } + } + else -> throw AssertionError("Unexpected variable type") + } + + private fun generateContainedMultiportTriggerFieldInit( + instName: String, + containerName: String, + triggerRef: TriggerRef, + port: Port + ) = + """| + |${instName}.${triggerRef.name}_width = ${port.width}; + |for (int j = 0; j<${port.width}; j++) { + | ${instName}.${triggerRef.name}[j] = ${containerName}.${triggerRef.name}[j]; + |} + """ + .trimMargin() + + private fun generateContainedTriggerFieldInit(instName: String, triggerRef: TriggerRef) : String{ + if (triggerRef.resolve() !is Port) { + throw AssertionError("Unexpected variable type") + } + + val port = triggerRef.resolve() as Port + + if (port.isMultiport) { + return generateContainedMultiportTriggerFieldInit( + instName, "&self->${port.container.lfName}[0]", triggerRef, port + ) + } else { + return "${instName}.${port.lfName} = self->${port.container.lfName}->${triggerRef.name};" + } + } + + private fun generateContainedBankTriggerFieldInit(instName: String, triggerRef: TriggerRef) : String { + if (triggerRef.resolve() !is Port) { + throw AssertionError("Unexpected variable type") + } + + val port = triggerRef.resolve() as Port + + if (port.isMultiport) { + return generateContainedMultiportTriggerFieldInit( + "${instName}[i]", + "&self->${port.container.lfName}[i]", + triggerRef, + port + ) + } else { + return "${instName}[i].${triggerRef.name} = self->${port.container.lfName}[i].${triggerRef.name};" + } + } + + private fun generateContainedReactorScope(triggers: List, inst: ReactorInstantiation) = + with(PrependOperator) { + """| + |// Generated struct providing access to ports of child reactor `${inst.name}` + |struct _${inst.reactor.codeType}_${inst.name} { + ${"| "..triggers.joinToString(separator = "\n") { generateContainedTriggerInScope(it) }} + |}; + |struct _${inst.reactor.codeType}_${inst.name} ${inst.name}; + ${"|"..triggers.joinToString(separator = "\n") {generateContainedTriggerFieldInit(inst.name, it)}} + """ + .trimMargin() + } + + private fun generateContainedBankScope(triggers: List, inst: ReactorInstantiation) = + with(PrependOperator) { + """| + |// Generated struct providing access to ports of child reactor `${inst.name}` + |struct _${inst.reactor.codeType}_${inst.name} { + ${"| "..triggers.joinToString(separator = "\n") { generateContainedTriggerInScope(it) }} + |}; + |struct _${inst.reactor.codeType}_${inst.name} ${inst.name}[${inst.codeWidth}]; + |size_t ${inst.name}_width = ${inst.codeWidth}; + |for (int i = 0; i<${inst.codeWidth}; i++) { + ${"| "..triggers.joinToString(separator = "\n") {generateContainedBankTriggerFieldInit(inst.name, it)}} + |} + """ + .trimMargin() + } + + private fun generateTriggersEffectsAndSourcesInScope(reaction: Reaction) = + reaction.allUncontainedTriggers + .plus(reaction.allUncontainedEffects) + .plus(reaction.allUncontainedSources) + .joinToString(separator = "\n") { it.scope.toString() } + + private fun generateContainedTriggersAndSourcesInScope(reaction: Reaction) = + reaction.allContainedEffectsTriggersAndSources.toList().joinToString(separator = "\n") { + if (it.first.codeWidth > 1) { + generateContainedBankScope(it.second, it.first) + } else { + generateContainedReactorScope(it.second, it.first) + } + } + + private fun generateTriggerRegisterEffect(reaction: Reaction) = + reaction.triggerRefs.joinToString( + separator = "\n", + ) { + registerEffect(it, reaction) + } + + private fun generateTriggerRegisterObserver(reaction: Reaction) = + reaction.sourcesRefs.joinToString( + separator = "\n", + ) { + registerObserver(it, reaction) + } + + private fun generateTriggerRegisterSource(reaction: Reaction) = + reaction.effectsRefs.joinToString( + separator = "\n", + ) { + registerSource(it, reaction) + } + + private fun generateReactorCtorCode(reaction: Reaction) = + with(PrependOperator) { + """ + |LF_INITIALIZE_REACTION(${reactor.codeType}, ${reaction.codeName}, ${reaction.deadline?.deadline?.toCCode()}); + ${" | "..generateTriggerRegisterEffect(reaction)} + ${" | "..generateTriggerRegisterSource(reaction)} + ${" | "..generateTriggerRegisterObserver(reaction)} + """ + .trimMargin() + } + + fun generateReactorCtorCodes() = + 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: ReactorInstantiation, port: OutputPort): List { + val res = mutableListOf() + for (reaction in reactor.reactions) { + if (reaction.allContainedTriggers.any { + it is TriggerRef && it.container == inst.reactor && it.resolve() == port + }) { + res.add(reaction) + } + } + return res + } + + fun getParentReactionObserversOfOutput(inst: ReactorInstantiation, port: OutputPort): List { + val res = mutableListOf() + for (reaction in reactor.reactions) { + if (reaction.allContainedSources.any { + it is TriggerRef && it.container == inst.reactor && it.resolve() == port + }) { + res.add(reaction) + } + } + return res + } + + fun getParentReactionSourcesOfInput(inst: ReactorInstantiation, port: InputPort): List { + val res = mutableListOf() + for (reaction in reactor.reactions) { + if (reaction.allContainedEffects.any { + it is TriggerRef && it.container == inst.reactor && it.resolve() == port + }) { + 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/uc2/UcReactorGenerator.kt similarity index 53% rename from lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt rename to lfc/core/src/main/kotlin/org/lflang/generator/uc2/UcReactorGenerator.kt index 43324fdcb..52d0bcb44 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc2/UcReactorGenerator.kt @@ -1,61 +1,23 @@ -package org.lflang.generator.uc +package org.lflang.generator.uc2 -import org.lflang.* import org.lflang.generator.PrependOperator -import org.lflang.generator.uc.UcActionGenerator.Companion.maxNumPendingEvents -import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeWidth -import org.lflang.generator.uc.UcPortGenerator.Companion.width -import org.lflang.generator.uc.UcReactorGenerator.Companion.hasPhysicalActions -import org.lflang.lf.* +import org.lflang.ir.Reactor +import org.lflang.toUnixString class UcReactorGenerator( private val reactor: Reactor, private val 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.allReactions - .filter { - it.triggers - .filter { it is BuiltinTriggerRef && it.type == BuiltinTrigger.SHUTDOWN } - .isNotEmpty() - } - .isNotEmpty() - - private fun numTriggers(): Int { - var res = - reactor.allActions.size + - reactor.allTimers.size + - reactor.allInputs.map { it.width }.sum() + - reactor.allOutputs.map { it.width }.sum() - if (hasShutdown) res++ - if (hasStartup) res++ - return res - } - - private val numChildren = reactor.allInstantiations.map { it.codeWidth }.sum() - private val parameters = UcParameterGenerator(reactor) - private val connections = UcConnectionGenerator(reactor, null, emptyList()) + private val connections = UcLocalConnectionGenerator(reactor) private val state = UcStateGenerator(reactor) private val ports = UcPortGenerator(reactor, connections) private val timers = UcTimerGenerator(reactor) private val actions = UcActionGenerator(reactor) private val reactions = UcReactionGenerator(reactor) - private val instances = - UcInstanceGenerator( - reactor, parameters, ports, connections, reactions, fileConfig, messageReporter) + private val instances = UcInstanceGenerator(reactor, parameters, ports, connections, reactions, fileConfig) private fun takesExtraParameters(): Boolean = parameters.generateReactorCtorDefArguments().isNotEmpty() || @@ -67,70 +29,11 @@ class UcReactorGenerator( else "LF_REACTOR_CTOR_SIGNATURE(${reactor.codeType})" fun generateReactorPrivatePreamble() = - reactor.allPreambles.joinToString( + reactor.preambles.joinToString( prefix = "// Private preambles\n", separator = "\n", postfix = "\n") { - it.code.toText() + it.code.toString() } - companion object { - val Reactor.codeType - get(): String = "Reactor_$name" - - val Reactor.includeGuard - get(): String = "LFC_GEN_${name.uppercase()}_H" - - val Reactor.hasStartup - get(): Boolean = - allReactions - .filter { - it.triggers - .filter { it is BuiltinTriggerRef && it.type == BuiltinTrigger.STARTUP } - .isNotEmpty() - } - .isNotEmpty() - - val Reactor.hasShutdown - get(): Boolean = - allReactions - .filter { - it.triggers - .filter { it is BuiltinTriggerRef && it.type == BuiltinTrigger.SHUTDOWN } - .isNotEmpty() - } - .isNotEmpty() - - fun Reactor.getEffects(v: Variable) = - allReactions.filter { it.triggers.filter { it.name == v.name }.isNotEmpty() } - - fun Reactor.getObservers(v: Variable) = - allReactions.filter { it.sources.filter { it.name == v.name }.isNotEmpty() } - - fun Reactor.getSources(v: Variable) = - allReactions.filter { it.effects.filter { it.name == v.name }.isNotEmpty() } - - fun Reactor.getEffects(v: BuiltinTrigger) = - allReactions.filter { it.triggers.filter { it.name == v.literal }.isNotEmpty() } - - fun Reactor.getObservers(v: BuiltinTrigger) = - allReactions.filter { it.sources.filter { it.name == v.literal }.isNotEmpty() } - - fun Reactor.hasPhysicalActions(): Boolean { - for (inst in allInstantiations) { - if (inst.reactor.hasPhysicalActions()) return true - } - return allActions.filter { it.isPhysical }.isNotEmpty() - } - } - - fun getMaxNumPendingEvents(): Int { - var numEvents = reactor.allTimers.count() - for (action in reactor.allActions) { - numEvents += action.maxNumPendingEvents - } - numEvents += connections.getMaxNumPendingEvents() - return numEvents - } - private fun generateReactorStruct() = with(PrependOperator) { """ @@ -144,7 +47,7 @@ class UcReactorGenerator( ${" | "..ports.generateReactorStructFields()} ${" | "..state.generateReactorStructFields()} ${" | "..parameters.generateReactorStructFields()} - | LF_REACTOR_BOOKKEEPING_INSTANCES(${reactor.allReactions.size}, ${numTriggers()}, ${numChildren}); + | LF_REACTOR_BOOKKEEPING_INSTANCES(${reactor.reactions.size}, ${reactor.numTriggers()}, ${reactor.numChildren}); |} ${reactor.codeType}; | """ @@ -214,4 +117,4 @@ class UcReactorGenerator( """ .trimMargin() } -} +} \ No newline at end of file diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc2/UcStateGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc2/UcStateGenerator.kt new file mode 100644 index 000000000..14c221b82 --- /dev/null +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc2/UcStateGenerator.kt @@ -0,0 +1,28 @@ +package org.lflang.generator.uc2 + +import org.lflang.ir.Reactor + +class UcStateGenerator(private val reactor: Reactor) { + fun generateReactorStructFields() = + reactor.stateVars.joinToString(prefix = "// State variables \n", separator = "\n") { + if (it.type.isArray) { + "${it.type.targetCode} ${it.lfName}[${it.type.arrayLength}];" + } else { + "${it.type.targetCode} ${it.lfName};" + } + } + + fun generateInitializeStateVars() = + reactor.stateVars + .filter { it.isInitialized } + .joinToString(prefix = "// Initialize State variables \n", separator = "\n") { + if (it.type.isArray) { + """|${it.type.targetCode} _${it.lfName}_init[${it.type.arrayLength}] = ${it.init}; + |memcpy(&self->${it.lfName}, &_${it.lfName}_init, sizeof(_${it.lfName}_init)); + """ + .trimMargin() + } else { + "self->${it.lfName} = ${it.init};" + } + } +} diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc2/UcTimerGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc2/UcTimerGenerator.kt new file mode 100644 index 000000000..4f83f86ef --- /dev/null +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc2/UcTimerGenerator.kt @@ -0,0 +1,37 @@ +package org.lflang.generator.uc2 + +import org.lflang.ir.Reactor +import org.lflang.ir.Timer + +class UcTimerGenerator(private val reactor: Reactor) { + private fun generateSelfStructs(timer: Timer) = + "LF_DEFINE_TIMER_STRUCT(${reactor.codeType}, ${timer.lfName}, ${timer.getEffects().size}, ${timer.getObservers().size});" + + private fun generateCtor(timer: Timer) = + "LF_DEFINE_TIMER_CTOR(${reactor.codeType}, ${timer.lfName}, ${timer.getEffects().size}, ${timer.getObservers().size});" + + fun generateCtors() = + reactor.timers.joinToString( + separator = "\n", prefix = "// Timer constructors \n", postfix = "\n") { + generateCtor(it) + } + + fun generateSelfStructs() = + reactor.timers.joinToString( + separator = "\n", prefix = "// Timer structs \n", postfix = "\n") { + generateSelfStructs(it) + } + + fun generateReactorStructFields() = + reactor.timers.joinToString(separator = "\n", prefix = "// Timers\n", postfix = "\n") { + "LF_TIMER_INSTANCE(${reactor.codeType}, ${it.lfName});" + } + + private fun generateReactorCtorCode(timer: Timer) = + "LF_INITIALIZE_TIMER(${reactor.codeType}, ${timer.lfName}, ${timer.offset.toCCode()}, ${timer.period.toCCode()});" + + fun generateReactorCtorCodes() = + reactor.timers.joinToString(separator = "\n", prefix = "// Initialize Timers\n") { + generateReactorCtorCode(it) + } +} diff --git a/lfc/core/src/main/kotlin/org/lflang/ir/Connection.kt b/lfc/core/src/main/kotlin/org/lflang/ir/Connection.kt new file mode 100644 index 000000000..4523ab1e4 --- /dev/null +++ b/lfc/core/src/main/kotlin/org/lflang/ir/Connection.kt @@ -0,0 +1,49 @@ +package org.lflang.ir + +enum class ConnectionKind { + CONNECTION, + DELAYED_CONNECTION, + PHYSICAL_CONNECTION, + FEDERATED_CONNECTION, +} + +class Connection( + val sourceRef: TriggerRef, + val targetPortRefs: List, + val kind: ConnectionKind, + val delay: TimeValue, + val isIterated: Boolean, + val bufferSize: Int, + val width: Int +) { + lateinit var source: Port + lateinit var targets: List + lateinit var container: Reactor + + val isPhysical: Boolean + get() = kind == ConnectionKind.PHYSICAL_CONNECTION + + val isDelayed: Boolean + get() = kind == ConnectionKind.DELAYED_CONNECTION + + val uid + get() : Int { + var uid = 0 + for (conn in container.connections) { + if (conn == this) { + return uid + } + uid++ + } + return -1 + } + + val numDownstreams + get() = targets.size + + val maxNumPendingEvents + get() = bufferSize //TODO: + + val isLogical + get() = !isPhysical && !isDelayed +} diff --git a/lfc/core/src/main/kotlin/org/lflang/ir/Federate.kt b/lfc/core/src/main/kotlin/org/lflang/ir/Federate.kt new file mode 100644 index 000000000..1f56b20af --- /dev/null +++ b/lfc/core/src/main/kotlin/org/lflang/ir/Federate.kt @@ -0,0 +1,13 @@ +package org.lflang.ir + +import org.lflang.target.property.type.PlatformType + +class Federate( + val maxWait: TimeValue, + val mainReactor: Reactor, + val platform: PlatformType.Platform, + val federatedEnvironment: FederatedEnvironment +) { + val instantiation get() : FederateInstantiation = federatedEnvironment.instantiation.first {it.federate == this } + +} diff --git a/lfc/core/src/main/kotlin/org/lflang/ir/File.kt b/lfc/core/src/main/kotlin/org/lflang/ir/File.kt new file mode 100644 index 000000000..882de92c2 --- /dev/null +++ b/lfc/core/src/main/kotlin/org/lflang/ir/File.kt @@ -0,0 +1,20 @@ +package org.lflang.ir + +import java.nio.file.Path + +class Preamble(val code: TargetCode) {} + +class Import( + val reactors: List, + val file: File +) {} + +class File( + val name: String, + val path: Path, + val imports: List, + val preambles: List, + val reactors: List, +) { + val genDir: Path = path.resolve("gen") +} diff --git a/lfc/core/src/main/kotlin/org/lflang/ir/Generator.kt b/lfc/core/src/main/kotlin/org/lflang/ir/Generator.kt new file mode 100644 index 000000000..97b508d6a --- /dev/null +++ b/lfc/core/src/main/kotlin/org/lflang/ir/Generator.kt @@ -0,0 +1,230 @@ +package org.lflang.ir + +import org.apache.commons.jxpath.ri.compiler.VariableReference +import org.eclipse.emf.ecore.resource.Resource +import org.lflang.allPreambles +import org.lflang.allStateVars +import org.lflang.generator.LocationInfo +import org.lflang.generator.locationInfo +import org.lflang.indexInContainer +import org.lflang.isOfTimeType +import org.lflang.isPhysical +import org.lflang.lf.Action +import org.lflang.lf.Code +import org.lflang.lf.Connection +import org.lflang.lf.Deadline +import org.lflang.lf.Expression +import org.lflang.lf.Import +import org.lflang.lf.Initializer +import org.lflang.lf.Input +import org.lflang.lf.Model +import org.lflang.lf.Tardy +import org.lflang.lf.Parameter +import org.lflang.lf.Preamble +import org.lflang.lf.Reaction +import org.lflang.lf.Reactor +import org.lflang.lf.StateVar +import org.lflang.lf.Time +import org.lflang.lf.Timer +import org.lflang.lf.TriggerRef +import org.lflang.lf.Type +import org.lflang.lf.VarRef +import org.lflang.target.property.type.PlatformType +import org.lflang.toText +import kotlin.collections.iterator +import kotlin.io.path.Path +import kotlin.time.Duration +import kotlin.time.Duration.Companion.nanoseconds + + +class ParseContext { + var reactors: HashMap = HashMap() +} + +fun fromAst(time: Time) : TimeValue = TimeValue(time.interval.nanoseconds) +fun Expression.toTimeValue() : TimeValue = TimeValue(timeInNanoseconds = Duration.parse(this.toString())) + +fun fromXText(type: Type?) : CType { + return if (type == null) { + CType( + targetCode = TargetCode(code = ""), + isArray = false, + arrayLength = 0, + isVoid = false + ) + } else { + CType( + targetCode = type.code.toIR(), + isArray = type.cStyleArraySpec != null, + arrayLength = type.cStyleArraySpec!!.length, + isVoid = false + ) + } +} + + +fun Action.toIR(reactor: org.lflang.ir.Reactor) : org.lflang.ir.Action = Action( + this.name, + kind = TriggerKind.ACTION, + isPhysical = isPhysical, + type = fromXText(this.type), + maxNumPendingEvents = 10, + minDelay = this.minDelay.toTimeValue(), + minSpacing = this.minSpacing.toTimeValue(), +) + +fun Timer.toIR(reactor: org.lflang.ir.Reactor) : org.lflang.ir.Timer = Timer( + lfName = this.name, + offset = fromAst(this.offset as Time), + period = fromAst(this.period as Time), + kind = TriggerKind.TIMER, +) + +fun Preamble.toIR() : org.lflang.ir.Preamble = Preamble(code=this.code.toIR()) +fun Code.toIR() : TargetCode = TargetCode(code=this.body.toString()) +fun String.toIR(): TargetCode = TargetCode(code=this) + +fun Parameter.toIR() : ConstructorParameters = ConstructorParameters( + lfName = this.name, + type= TargetCode(code = this.type.toString()), + defaultValue = this.init.toText().toIR(), + isTime = this.isOfTimeType, + defaultValueAsTimeValue = null, //TODO: FIX ME +) + +fun Initializer.toIR() : TargetCode = TargetCode(code=this.toString()) + +fun StateVar.toIR() : StateVariable = StateVariable( + lfName = this.name, + type = fromXText(this.type), + init = this.init.toIR(), + isInitialized = this.init.isAssign, //TODO: fix +) + +fun TriggerRef.toIR(reactor: org.lflang.ir.Reactor) : org.lflang.ir.TriggerRef { + return when (this.toString()) { + "startup" -> StartupTriggerRef( + container = reactor + ) + "shutdown" -> ShutdownTriggerRef( + container = reactor + ) + else -> throw IllegalArgumentException("Unknown BuiltInTrigger Ref '$this'") + } +} + +fun LocationInfo.toIR(): ReactorLocationInformation = ReactorLocationInformation( + line = this.line, + column = this.column, + file = Path(this.fileName), +) + +fun Tardy.toIR() : TardyDeliveryReaction = TardyDeliveryReaction( + body = this.code.toIR() +) + +fun Reaction.toIR(reactor: org.lflang.ir.Reactor) : org.lflang.ir.Reaction = Reaction( + idx = this.indexInContainer, + body = this.code.toIR(), + triggerRefs = this.triggers.map { it.toIR(reactor) }, + sourcesRefs = this.sources.map { it.toIR(reactor) }, + effectsRefs = this.effects.map { it.toIR(reactor) }, + loc = this.locationInfo().toIR(), + container = reactor, + deadline = this.deadline?.toIR(), + tardy = this.tardy.toIR(), +) + +fun Deadline.toIR() : DeadlineReaction = DeadlineReaction( + body = this.code.toIR(), + deadline = this.delay.toString().toTime(), +) + +fun VarRef.toIR(container: org.lflang.ir.Reactor) : org.lflang.ir.TriggerRef { + if (this.variable == null) { + return VariableNameTriggerRef( + container = container, + variable = container.triggers.first { it.lfName == this.variable.name } + ) + } else { + val instantiation = container.childReactors.first { it.name == this.variable.name}; + return VariableContainedTriggerRef( + container = container, + variable = instantiation.reactor.triggers.first { it.lfName == this.variable.name }, + instance = instantiation.name + ) + } +} + +fun Connection.toIR(container: org.lflang.ir.Reactor) : List { + var conns = mutableListOf(); + for (left in this.leftPorts) { + conns.add(Connection( + sourceRef = left.toIR(container), + targetPortRefs = this.rightPorts.map { it.toIR(container) }, + kind = if (this.isPhysical) ConnectionKind.PHYSICAL_CONNECTION else (if (this.delay == null) ConnectionKind.CONNECTION else ConnectionKind.DELAYED_CONNECTION), + delay = this.delay.toTimeValue(), + isIterated = this.isIterated, + bufferSize = 10, + width = this.leftPorts.size, + )) + } + return conns; +} + +fun Input.toIR() : InputPort = InputPort( + lfName = this.name, + dataType = fromXText(this.type), + kind = TriggerKind.INPUT, + isMultiport = TODO(), + isInterleaved = TODO(), + width = TODO(), +) + + +fun Type.toIR() : TargetCode = TargetCode(code = this.code.toString()) + +fun Reactor.toIR(environment: Environment) : org.lflang.ir.Reactor = + Reactor( + lfName = this.name ?: "Reactor", + fullyQualifiedName = this.name ?: "Reactor", + isMain = this.isMain, + preambles = this.allPreambles.map { it.toIR() }, + ctorParams = this.parameters.map { it.toIR() }, + stateVars = this.allStateVars.map { it.toIR() }, + childReactors = mutableListOf(), //TODO: + location = this.locationInfo().toIR(), + env = environment, + federate = null, + parentReactor = null, + ) + +fun Import.toIR() : org.lflang.ir.Import = Import( + reactors = TODO(), + file = TODO() +) + +fun Model.toIR(environment: Environment) : File = File( + name = this.toString(), + imports = this.imports.map { it.toIR() }, + preambles = this.preambles.map { it.toIR() }, + reactors = this.reactors.map { it.toIR(environment) }, + path = Path("test"), +) + +class XTextConverter(val resource: Resource) { + fun convert() : Pair { + var files = mutableListOf() + var env = Environment() + for (obj in resource.contents) { + files.add((obj as Model).toIR(env)) + println(obj.eClass()) + } + + var reactors = files.flatMap { it.reactors } + + env.mainReactor = reactors.get(0) + + return Pair(env, files.get(0)) + } +} diff --git a/lfc/core/src/main/kotlin/org/lflang/ir/LocationInformation.kt b/lfc/core/src/main/kotlin/org/lflang/ir/LocationInformation.kt new file mode 100644 index 000000000..ad2263a51 --- /dev/null +++ b/lfc/core/src/main/kotlin/org/lflang/ir/LocationInformation.kt @@ -0,0 +1,10 @@ +package org.lflang.ir + +import java.nio.file.Path + +data class ReactorLocationInformation( + //val declaredIn: File, + val line: Int, + val column: Int, + val file: Path +) diff --git a/lfc/core/src/main/kotlin/org/lflang/ir/Port.kt b/lfc/core/src/main/kotlin/org/lflang/ir/Port.kt new file mode 100644 index 000000000..b1032e7ec --- /dev/null +++ b/lfc/core/src/main/kotlin/org/lflang/ir/Port.kt @@ -0,0 +1,74 @@ +package org.lflang.ir + +import kotlin.time.Duration + +open class Port( + override val kind: TriggerKind = TriggerKind.INPUT, + override val lfName: String, + open val dataType: CType, + val isMultiport: Boolean, + val isInterleaved: Boolean, + val width: Int +) : Trigger(lfName, kind) { + val externalArgs + get(): String = "_${lfName}_external" + + // val width + // get(): Int = widthSpec?.getWidth() ?: 1 + + val maxWait: TimeValue + get(): TimeValue = container.federate?.maxWait ?: TimeValue(timeInNanoseconds = Duration.parse("10ms")) + + val codeType: String = this.lfName + + + fun triggerRef(r: Reactor) : TriggerRef { + if (r == this.container) { + return VariableNameTriggerRef( + container = r, + variable = this + ) + } else { + return VariableContainedTriggerRef( + container = container, + variable = this, + instance = container.instantiation.name + ) + } + } + + /* + val Type.isArray + get(): Boolean = cStyleArraySpec != null + + val Type.arrayLength + get(): Int = cStyleArraySpec.length */ +} + +class Multiport( + override val lfName: String, + override val kind: TriggerKind, + val ports: List, +) : Trigger(lfName, kind) {} + +class InputPort( + override val kind: TriggerKind = TriggerKind.INPUT, + override val lfName: String, + override val dataType: CType, + isMultiport: Boolean, + isInterleaved: Boolean, + width: Int, +) : Port(kind, lfName, dataType, isMultiport, isInterleaved, width) { + lateinit var incomingConnections: List +} + +class OutputPort( + override val lfName: String, + override val kind: TriggerKind = TriggerKind.OUTPUT, + override val dataType: CType, + isMultiport: Boolean, + isInterleaved: Boolean, + width: Int, +) : Port(kind, lfName, dataType, isMultiport, isInterleaved, width) { + lateinit var outgoingConnections: List +} diff --git a/lfc/core/src/main/kotlin/org/lflang/ir/Reaction.kt b/lfc/core/src/main/kotlin/org/lflang/ir/Reaction.kt new file mode 100644 index 000000000..bb3db38ca --- /dev/null +++ b/lfc/core/src/main/kotlin/org/lflang/ir/Reaction.kt @@ -0,0 +1,125 @@ +package org.lflang.ir + +data class Reaction( + val idx: Int, + val body: TargetCode, + val triggerRefs: List, + val sourcesRefs: List, + val effectsRefs: List, + val container: Reactor, + val loc: ReactorLocationInformation, //TODO: + //val maxWait: MaxWaitReaction?, + val deadline: DeadlineReaction?, + val tardy: TardyDeliveryReaction? +) { + lateinit var sources: List + lateinit var effects: List + lateinit var observers: List + + val triggers: List + get() = sources + effects + observers + + val codeName + get(): String = "reaction$index" + + val nameInReactor + get(): String = "self->${codeName}" + + val index + get(): Int { + var idx = 0 + for (r in this.container.reactions) { + if (this == r) { + break + } + idx += 1 + } + return idx + } + + + val allContainedTriggers + get() = triggerRefs.filter { it is VariableContainedTriggerRef } + + val allContainedEffects + get() = effectsRefs.filter { it is VariableContainedTriggerRef } + + val allContainedSources + get() = sourcesRefs.filter { it is VariableContainedTriggerRef } + + + val allUncontainedTriggers + get() = triggerRefs.filter { it is VariableNameTriggerRef } + + val allUncontainedEffects + get() = effectsRefs.filter { it is VariableNameTriggerRef } + + val allUncontainedSources + get() = sourcesRefs.filter { it is VariableNameTriggerRef } + + // Calculate the total number of effects, considering that we might write to + // a contained input port + val totalNumEffects + get(): Int { + var res = 0 + for (effect in allUncontainedEffects) { + val variable = effect.resolve() + res += + if (variable is Port) { + variable.width + } else { + 1 + } + } + for (effect in allContainedEffects) { + res += effect.container.codeWidth * (effect.resolve() as Port).width + } + return res + } + + val allContainedEffectsTriggersAndSources + get() = run { + val res = mutableMapOf>() + for (triggerRef in allContainedEffects.plus(allContainedSources).plus(allContainedTriggers)) { + val containedTriggerRef = triggerRef as VariableContainedTriggerRef + if (containedTriggerRef.container.instantiation !in res.keys) { + res[containedTriggerRef.container.instantiation] = mutableListOf() + } + + res[containedTriggerRef.container.instantiation] = res[containedTriggerRef.container.instantiation]!!.plus(triggerRef) + } + res + } + + + val ctorDeadlineArgs + get() = + if (this.deadline != null) + "LF_REACTION_TYPE(${container.codeType}, ${codeName}_deadline_violation_handler) " + else "NULL" + + val ctorStpArgs + get() = + if (this.tardy != null) + "LF_REACTION_TYPE(${container.codeType}, ${codeName}_stp_violation_handler)" + else "NULL" + +} + +fun Reaction.resolveTriggers() { + // TODO: initialize triggers here +} + +data class DeadlineReaction( + val body: TargetCode, + val deadline: TimeValue, +) + +data class TardyDeliveryReaction( + val body: TargetCode, +) + +data class MaxWaitReaction( + val body: TargetCode, + val maxWait: TimeValue, +) diff --git a/lfc/core/src/main/kotlin/org/lflang/ir/Reactor.kt b/lfc/core/src/main/kotlin/org/lflang/ir/Reactor.kt new file mode 100644 index 000000000..12fb77014 --- /dev/null +++ b/lfc/core/src/main/kotlin/org/lflang/ir/Reactor.kt @@ -0,0 +1,121 @@ +package org.lflang.ir + +data class TargetCode(val code: String) + +data class StateVariable( + val lfName: String, + val type: CType, + val init: TargetCode, + val isInitialized: Boolean +) + +data class ConstructorParameters( + val lfName: String, + val type: TargetCode, + val defaultValue: TargetCode?, + val isTime: Boolean, + val defaultValueAsTimeValue: TimeValue?, +) + +class FederatedEnvironment(val instantiation: List) + +class FederateInstantiation( + val name: String, + val federate: Federate, + val codeWidth: Int +) { +} + +class ReactorInstantiation( + val name: String, + val reactor: Reactor, + val codeWidth: Int, + val allParameters: List = listOf(), +) { + +} + +class Environment { + lateinit var mainReactor: Reactor +} + +class Parameter( + val name: String, + val type: CType, + val init: TargetCode, + val value: TargetCode +) + +class Reactor( + val lfName: String, + val env: Environment, + val federate: Federate?, + val fullyQualifiedName: String, + val isMain: Boolean, + val preambles: List, + val ctorParams: List, + val childReactors: List, + var parentReactor: Reactor? = null, + val stateVars: List, + val location: ReactorLocationInformation, + +) { + lateinit var ports: List + lateinit var triggers: Set + lateinit var reactions: List + lateinit var connections: List + + val codeType: String = "Reactor_$lfName" + val includeGuard: String = "LFC_GEN_${lfName.uppercase()}_H" + + val hasStartup + get(): Boolean = triggers.any { it.kind == TriggerKind.STARTUP } + + val hasShutdown + get(): Boolean = triggers.any { it.kind == TriggerKind.SHUTDOWN } + + val startup + get(): Startup? = triggers.find { it.kind == TriggerKind.STARTUP } as Startup? + + val shutdown + get(): Shutdown? = triggers.find { it.kind == TriggerKind.SHUTDOWN } as Shutdown? + + val actions + get(): List = triggers.filter { it.kind == TriggerKind.ACTION } as List + + val inputs + get(): List = triggers.filter { it.kind == TriggerKind.INPUT } as List + + val outputs + get(): List = triggers.filter { it.kind == TriggerKind.OUTPUT } as List + + val timers + get(): List = triggers.filter { it.kind == TriggerKind.TIMER } as List + + val instantiation + get(): ReactorInstantiation = parentReactor?.childReactors?.first { it.reactor == this} ?: throw IllegalStateException("No Reactor") + + val codeWidth + get() : Int = instantiation.codeWidth + + fun numTriggers(): Int { + var res = + actions.size + + timers.size + + inputs.sumOf { it.width } + + outputs.sumOf { it.width } + if (hasShutdown) res++ + if (hasStartup) res++ + return res + } + + + val numChildren = childReactors.sumOf { it.codeWidth } + + fun hasPhysicalActions(): Boolean { + for (inst in childReactors) { + if (inst.reactor.hasPhysicalActions()) return true + } + return triggers.any { it.kind == TriggerKind.ACTION && (it as Action).isPhysical } + } +} diff --git a/lfc/core/src/main/kotlin/org/lflang/ir/Time.kt b/lfc/core/src/main/kotlin/org/lflang/ir/Time.kt new file mode 100644 index 000000000..540383418 --- /dev/null +++ b/lfc/core/src/main/kotlin/org/lflang/ir/Time.kt @@ -0,0 +1,9 @@ +package org.lflang.ir + +import kotlin.time.Duration + +data class TimeValue(val timeInNanoseconds: Duration) { + fun toCCode(): String = "NSEC($timeInNanoseconds)" +} + +fun String.toTime(): TimeValue = TimeValue(timeInNanoseconds = Duration.parse(this)) diff --git a/lfc/core/src/main/kotlin/org/lflang/ir/Trigger.kt b/lfc/core/src/main/kotlin/org/lflang/ir/Trigger.kt new file mode 100644 index 000000000..2afd41102 --- /dev/null +++ b/lfc/core/src/main/kotlin/org/lflang/ir/Trigger.kt @@ -0,0 +1,151 @@ +package org.lflang.ir + +enum class TriggerKind { + TIMER, + ACTION, + INPUT, + OUTPUT, + STARTUP, + SHUTDOWN +} + +class CType( + val targetCode: org.lflang.ir.TargetCode, + val isArray: Boolean, + val arrayLength: Int, + val isVoid: Boolean +) + +abstract class TriggerRef(open val container: Reactor) { + abstract fun resolve(): Trigger + + val name get() : String = when (this) { + is VariableNameTriggerRef -> this.variable.lfName + is VariableContainedTriggerRef -> this.variable.lfName + is ShutdownTriggerRef -> "shutdown" + is StartupTriggerRef -> "startup" + else -> throw IllegalArgumentException("Unknown trigger ref $this") + } + + fun isEffectOf(reaction: Reaction): Boolean = + reaction.effects.any { it == this.resolve() } +} + +class StartupTriggerRef(container: Reactor) : TriggerRef(container) { + override fun resolve(): Startup { + val foundTrigger = container.triggers.find { it.kind == TriggerKind.STARTUP} + + if (foundTrigger == null) { + throw IllegalArgumentException( + "Trigger shutdown has not been found inside reactor ${container.lfName}") + } + + return foundTrigger as Startup + } +} + +class ShutdownTriggerRef(container: Reactor) : TriggerRef(container) { + override fun resolve(): Shutdown { + val foundTrigger = container.triggers.find { it.kind == TriggerKind.SHUTDOWN} + + if (foundTrigger == null) { + throw IllegalArgumentException( + "Trigger shutdown has not been found inside reactor ${container.lfName}") + } + + return foundTrigger as Shutdown + } +} + +data class VariableNameTriggerRef(override val container: Reactor, val variable: Trigger) : + TriggerRef(container) { + override fun resolve(): Trigger { + val foundTrigger = container.triggers.find { it.lfName == this.variable.lfName } + + if (foundTrigger == null) { + throw IllegalArgumentException( + "Trigger '$variable' has not been found inside reactor ${container.lfName}") + } + + return foundTrigger + } + +} + +data class VariableContainedTriggerRef( + override val container: Reactor, + val variable: Trigger, + val instance: String +) : TriggerRef(container) { + override fun resolve(): Trigger { + val foundTrigger = container.childReactors.first { it.name == instance }.reactor.triggers.find { it.lfName == variable.lfName } + + if (foundTrigger == null) { + throw IllegalArgumentException( + "Trigger '$instance.$variable' has not been found inside reactor ${container.lfName}") + } + + return foundTrigger + } + +} + +open class Trigger( + open val lfName: String, + open val kind: TriggerKind +) { + + open lateinit var container: Reactor + + + //fun setContainer(reactor: Reactor) { + // this.container = reactor + //} + + /** Reactions triggered, by this trigger */ + val getEffects + get() = { this.container.reactions.filter { it.effects.contains(this) } } + + /** Reactions that can trigger, this trigger */ + val getSources + get() = { this.container.reactions.filter { it.sources.contains(this) } } + + /** Reactions that can observe this trigger */ + val getObservers + get() = { this.container.reactions.filter { it.observers.contains(this) } } + + + fun isEffectOf(reaction: Reaction): Boolean = + reaction.effects.any { it == this } +} + +class Timer( + override val lfName: String, + override val kind: TriggerKind = TriggerKind.TIMER, + val offset: TimeValue, + val period: TimeValue +) : Trigger(lfName, kind) {} + +class Action( + override val lfName: String, + override val kind: TriggerKind = TriggerKind.ACTION, + val isPhysical: Boolean = false, + val type: CType, + val maxNumPendingEvents: Int = 1, + val minDelay: TimeValue, + val minSpacing: TimeValue, +) : Trigger(lfName, kind) { + /** Returns the C Enum representing the type of action. */ + val actionType + get(): String = if (isPhysical) "PhysicalAction" else "LogicalAction" +} + +class Startup( + override val lfName: String, + override val kind: TriggerKind = TriggerKind.STARTUP, +) : Trigger(lfName, kind) {} + +class Shutdown( + override val lfName: String, + override val kind: TriggerKind = TriggerKind.SHUTDOWN, +) : Trigger(lfName, kind) {} diff --git a/src/startup_coordinator.c b/src/startup_coordinator.c index 54e6eaad7..e506f8fed 100644 --- a/src/startup_coordinator.c +++ b/src/startup_coordinator.c @@ -368,7 +368,7 @@ static void StartupCoordinator_handle_start_time_request(StartupCoordinator *sel } while (ret != LF_OK); // We now schedule a system event here, because otherwise we will never detect no other federates responding - StartupCoordinator_schedule_system_self_event(self, self->env->get_physical_time(self->env) + TRANSIENT_WAIT_TIME, + StartupCoordinator_schedule_system_self_event(self, self->env->get_physical_time(self->env) + MSEC(250), StartupCoordination_start_time_response_tag); } @@ -447,7 +447,7 @@ static void StartupCoordinator_handle_start_time_response(StartupCoordinator *se Environment_schedule_timers(self->env, self->env->main, start_tag); self->env->scheduler->prepare_timestep(self->env->scheduler, start_tag); self->env->scheduler->set_and_schedule_start_tag(self->env->scheduler, joining_time); - } else if (self->joining_policy == JOIN_TIMER_ALIGNED) { + } else if (self->joining_policy == JOIN_ALIGNED_WITH_SHORT_TIMER) { joining_time = max_logical_time + MSEC(50); tag_t start_tag = {.time = joining_time, .microstep = 0}; LF_INFO(FED, "Policy: Timer Aligned Scheduling join_time: " PRINTF_TIME, joining_time); diff --git a/test/lf/src/FederatedMaxWaitNoHandler.lf b/test/lf/src/FederatedMaxWaitNoHandler.lf index d3db5b8a4..82d9e73bb 100644 --- a/test/lf/src/FederatedMaxWaitNoHandler.lf +++ b/test/lf/src/FederatedMaxWaitNoHandler.lf @@ -43,4 +43,4 @@ federated reactor { @maxwait(forever) r2 = new Dst() r1.out -> r2.in1 -} \ No newline at end of file +} diff --git a/test/unit/request_shutdown_test.c b/test/unit/request_shutdown_test.c index 6fb46c028..5b311d7a8 100644 --- a/test/unit/request_shutdown_test.c +++ b/test/unit/request_shutdown_test.c @@ -32,4 +32,4 @@ int main() { UNITY_BEGIN(); RUN_TEST(test_run); return UNITY_END(); -} \ No newline at end of file +}