diff --git a/lfc/core/src/main/java/org/lflang/validation/AttributeSpec.java b/lfc/core/src/main/java/org/lflang/validation/AttributeSpec.java index 094fcdf2..105a03bb 100644 --- a/lfc/core/src/main/java/org/lflang/validation/AttributeSpec.java +++ b/lfc/core/src/main/java/org/lflang/validation/AttributeSpec.java @@ -253,6 +253,19 @@ enum AttrParamType { new AttributeSpec(List.of(new AttrParamSpec(VALUE_ATTR, AttrParamType.INT, false)))); // @interface:tcp(name="string", address="string") e.g. @interface:tcp(name="if1", // address="127.0.0.1") + + ATTRIBUTE_SPECS_BY_NAME.put( + "interface_uart", + new AttributeSpec( + List.of( + new AttrParamSpec("name", AttrParamType.STRING, true), + new AttrParamSpec("uart_device", AttrParamType.INT, true), + new AttrParamSpec("baud_rate", AttrParamType.INT, true), + new AttrParamSpec("data_bits", AttrParamType.STRING, true), + new AttrParamSpec("parity", AttrParamType.STRING, true), + new AttrParamSpec("stop_bits", AttrParamType.STRING, true) + ))); + ATTRIBUTE_SPECS_BY_NAME.put( "interface_tcp", new AttributeSpec( 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 index cc6d3a72..012690b0 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcActionGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcActionGenerator.kt @@ -3,13 +3,13 @@ package org.lflang.generator.uc import org.lflang.* import org.lflang.generator.PrependOperator import org.lflang.generator.orZero -import org.lflang.generator.uc.UcReactorGenerator.Companion.hasStartup -import org.lflang.generator.uc.UcReactorGenerator.Companion.hasShutdown 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.AttributeUtils.getMaxNumberOfPendingEvents +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) { @@ -22,61 +22,72 @@ class UcActionGenerator(private val reactor: Reactor) { } } - /** Returns the C Enum representing the type of action.*/ - private val Action.actionType - get(): String = if (isPhysical) "PHYSICAL_ACTION" else "LOGICAL_ACTION" + /** Returns the C Enum representing the type of action. */ + private val Action.actionType + get(): String = if (isPhysical) "PHYSICAL_ACTION" else "LOGICAL_ACTION" - private fun generateSelfStruct(action: Action) = with(PrependOperator) { + private fun generateSelfStruct(action: Action) = + with(PrependOperator) { """ |LF_DEFINE_ACTION_STRUCT${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() - } + """ + .trimMargin() + } - private fun generateCtor(action: Action) = with(PrependOperator) { + 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() - } + """ + .trimMargin() + } - private fun generateCtor(builtin: BuiltinTrigger) = - (if (builtin == BuiltinTrigger.STARTUP) "LF_DEFINE_STARTUP_CTOR" else "LF_DEFINE_SHUTDOWN_CTOR") + - "(${reactor.codeType});\n" + 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 + } - 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 + } - 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 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 + } - 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; - } + private fun generateReactorCtorCode(action: Action) = + "LF_INITIALIZE_ACTION(${reactor.codeType}, ${action.name}, ${action.minDelay.orZero().toCCode()}, ${action.minSpacing.orZero().toCCode()});" - 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 generateReactorCtorCodeStartup() = "LF_INITIALIZE_STARTUP(${reactor.codeType});" - 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});" + 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)} 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 index 7f4f5f1e..5e946aef 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt @@ -1,32 +1,37 @@ - package org.lflang.generator.uc +import java.nio.file.Path +import kotlin.io.path.name +import kotlin.math.max import org.lflang.* -import org.lflang.target.TargetConfig import org.lflang.generator.PrependOperator -import org.lflang.generator.uc.UcInstanceGenerator.Companion.isAFederate import org.lflang.lf.Instantiation -import org.lflang.lf.Reactor +import org.lflang.target.TargetConfig import org.lflang.target.property.BuildTypeProperty import org.lflang.target.property.CmakeIncludeProperty import org.lflang.target.property.LoggingProperty import org.lflang.target.property.PlatformProperty import org.lflang.target.property.type.PlatformType -import org.lflang.util.FileUtil -import java.nio.file.Path -import java.time.LocalDateTime -import kotlin.io.path.name -import kotlin.math.max -abstract class UcCmakeGenerator(private val targetConfig: TargetConfig, private val fileConfig: UcFileConfig, private val numEvents: Int, private val numReactions: Int) { - 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 generateCmake(sources: List): String +abstract class UcCmakeGenerator( + private val targetConfig: TargetConfig, + private val fileConfig: UcFileConfig, + private val numEvents: Int, + private val numReactions: Int +) { + 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 - protected fun generateCmakeCommon(sources: List, compileDefs: List) = with(PrependOperator) { + abstract fun generateCmake(sources: List): String + + protected fun generateCmakeCommon(sources: List, compileDefs: List) = + with(PrependOperator) { """ | |set(LFC_GEN_SOURCES ${" | "..sources.filterNot{it.name == "lf_main.c"}.joinWithLn { "$S{CMAKE_CURRENT_LIST_DIR}/${it.toUnixString()}"}} @@ -39,10 +44,12 @@ abstract class UcCmakeGenerator(private val targetConfig: TargetConfig, private |set(LFC_GEN_INCLUDE_DIRS $S{CMAKE_CURRENT_LIST_DIR}) |set(REACTION_QUEUE_SIZE ${max(numReactions, 1)} CACHE STRING "Size of the reaction queue") |set(EVENT_QUEUE_SIZE ${max(numEvents, 2)} CACHE STRING "Size of the event queue") - """.trimMargin() - } + """ + .trimMargin() + } - protected fun generateCmakePosix(sources: List, compileDefs: List) = with(PrependOperator) { + protected fun generateCmakePosix(sources: List, compileDefs: List) = + with(PrependOperator) { """ |cmake_minimum_required(VERSION $minCmakeVersion) |project(${mainTarget} LANGUAGES C) @@ -61,28 +68,41 @@ abstract class UcCmakeGenerator(private val targetConfig: TargetConfig, private |target_link_libraries($S{LF_MAIN_TARGET} PRIVATE reactor-uc) |target_include_directories($S{LF_MAIN_TARGET} PRIVATE $S{LFC_GEN_INCLUDE_DIRS}) ${" |"..(includeFiles?.joinWithLn { "include(\"$it\")" } ?: "")} - """.trimMargin() - } + """ + .trimMargin() + } } -class UcCmakeGeneratorNonFederated(private val mainDef: Instantiation, targetConfig: TargetConfig, fileConfig: UcFileConfig, numEvents: Int, numReactions: Int): UcCmakeGenerator(targetConfig, fileConfig, numEvents, numReactions) { - override val mainTarget = fileConfig.name +class UcCmakeGeneratorNonFederated( + private val mainDef: Instantiation, + targetConfig: TargetConfig, + fileConfig: UcFileConfig, + numEvents: Int, + numReactions: Int +) : UcCmakeGenerator(targetConfig, fileConfig, numEvents, numReactions) { + override val mainTarget = fileConfig.name - override fun generateCmake(sources: List) = - if (platform == PlatformType.Platform.NATIVE) { - generateCmakePosix(sources, emptyList()) - } else { - generateCmakeCommon(sources, emptyList()) - } + override fun generateCmake(sources: List) = + if (platform == PlatformType.Platform.NATIVE) { + generateCmakePosix(sources, emptyList()) + } else { + generateCmakeCommon(sources, emptyList()) + } } -class UcCmakeGeneratorFederated(private val federate: UcFederate, targetConfig: TargetConfig, fileConfig: UcFileConfig, numEvents: Int, numReactions: Int): UcCmakeGenerator(targetConfig, fileConfig, numEvents, numReactions) { - override val mainTarget = federate.codeType +class UcCmakeGeneratorFederated( + private val federate: UcFederate, + targetConfig: TargetConfig, + fileConfig: UcFileConfig, + numEvents: Int, + numReactions: Int +) : UcCmakeGenerator(targetConfig, fileConfig, numEvents, numReactions) { + override val mainTarget = federate.codeType - override fun generateCmake(sources: List) = - if (platform == PlatformType.Platform.NATIVE) { - generateCmakePosix(sources, federate.getCompileDefs()) - } else { - generateCmakeCommon(sources, federate.getCompileDefs()) - } + override fun generateCmake(sources: List) = + if (platform == PlatformType.Platform.NATIVE) { + generateCmakePosix(sources, federate.getCompileDefs()) + } else { + generateCmakeCommon(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 index 387eb703..40133625 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt @@ -8,386 +8,405 @@ 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 + * 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. + private val currentFederate: + UcFederate?, // The federate to generate connections for. If set then `reactor` should be + // the top-level reactor. + private val allFederates: + List< + UcFederate> // 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 + /** 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.federate == c.src.federate && + it.dest.federate == c.dest.federate && + it.getChannelType() == c.getChannelType() + } - private val isFederated = currentFederate != null + 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, + ) - /** - * 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 + 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 } - // 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 + 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() /** - * Given a list of ConnectionChannels, group them together. How they are grouepd depends on - * whether we are dealing with federated or non-federated reactors. + * Whether we have initialized the UcFederates with NetworkInterfaces. This is only done once. */ - 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.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 var federateInterfacesInitialized = false + /** + * A global list of FederatedConnectionBundles. It is computed once and reused when + * code-generating */ - 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()) - } - } + private var allFederatedConnectionBundles: List = 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() - - while (groupedSet.isNotEmpty()) { - val g = groupedSet.first()!! - val toRemove = mutableListOf(g) - when (g) { - is UcFederatedGroupedConnection -> { - val group = groupedSet.filterIsInstance().filter { - (it.srcFed == g.srcFed && it.destFed == g.destFed) || - (it.srcFed == g.destFed && it.destFed == g.srcFed) - } - - bundles.add( - UcFederatedConnectionBundle( - g.srcFed, g.destFed, group - ) - ) - - toRemove.addAll(group) - } + /** + * 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() + + while (groupedSet.isNotEmpty()) { + val g = groupedSet.first()!! + val toRemove = mutableListOf(g) + when (g) { + is UcFederatedGroupedConnection -> { + val group = + groupedSet.filterIsInstance().filter { + (it.srcFed == g.srcFed && it.destFed == g.destFed) || + (it.srcFed == g.destFed && it.destFed == g.srcFed) } - 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 - } + bundles.add(UcFederatedConnectionBundle(g.srcFed, g.destFed, group)) - // 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) + 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) + } - 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 - } - } + // 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 - } - } + // 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 + } } + 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 generateLogicalSelfStruct(conn: UcGroupedConnection) = + "LF_DEFINE_LOGICAL_CONNECTION_STRUCT(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.numDownstreams()});" - private fun generateDelayedSelfStruct(conn: UcGroupedConnection) = - "LF_DEFINE_DELAYED_CONNECTION_STRUCT(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.numDownstreams()}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents}, ${conn.delay});" + private fun generateLogicalCtor(conn: UcGroupedConnection) = + "LF_DEFINE_LOGICAL_CONNECTION_CTOR(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.numDownstreams()});" - private fun generateDelayedCtor(conn: UcGroupedConnection) = - "LF_DEFINE_DELAYED_CONNECTION_CTOR(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.numDownstreams()}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents}, ${conn.delay}, ${conn.isPhysical});" + private fun generateDelayedSelfStruct(conn: UcGroupedConnection) = + "LF_DEFINE_DELAYED_CONNECTION_STRUCT(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.numDownstreams()}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents}, ${conn.delay});" - private fun generateFederatedInputSelfStruct(conn: UcGroupedConnection) = - "LF_DEFINE_FEDERATED_INPUT_CONNECTION_STRUCT(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents});" + private fun generateDelayedCtor(conn: UcGroupedConnection) = + "LF_DEFINE_DELAYED_CONNECTION_CTOR(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.numDownstreams()}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents}, ${conn.delay}, ${conn.isPhysical});" - private fun generateFederatedInputCtor(conn: UcGroupedConnection) = - "LF_DEFINE_FEDERATED_INPUT_CONNECTION_CTOR(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents}, ${conn.delay}, ${conn.isPhysical});" + private fun generateFederatedInputSelfStruct(conn: UcGroupedConnection) = + "LF_DEFINE_FEDERATED_INPUT_CONNECTION_STRUCT(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents});" - private fun generateFederatedOutputSelfStruct(conn: UcGroupedConnection) = - "LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_STRUCT(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.srcPort.type.toText()});" + private fun generateFederatedInputCtor(conn: UcGroupedConnection) = + "LF_DEFINE_FEDERATED_INPUT_CONNECTION_CTOR(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents}, ${conn.delay}, ${conn.isPhysical});" - private fun generateFederatedOutputCtor(conn: UcGroupedConnection) = - "LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_CTOR(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.srcPort.type.toText()});" + private fun generateFederatedOutputSelfStruct(conn: UcGroupedConnection) = + "LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_STRUCT(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.srcPort.type.toText()});" - private fun generateFederatedConnectionSelfStruct(conn: UcFederatedGroupedConnection) = - if (conn.srcFed == currentFederate) generateFederatedOutputSelfStruct(conn) else generateFederatedInputSelfStruct( - conn - ) + private fun generateFederatedOutputCtor(conn: UcGroupedConnection) = + "LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_CTOR(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.srcPort.type.toText()});" - private fun generateFederatedConnectionCtor(conn: UcFederatedGroupedConnection) = - if (conn.srcFed == currentFederate) generateFederatedOutputCtor(conn) else generateFederatedInputCtor(conn) + private fun generateFederatedConnectionSelfStruct(conn: UcFederatedGroupedConnection) = + if (conn.srcFed == currentFederate) generateFederatedOutputSelfStruct(conn) + else generateFederatedInputSelfStruct(conn) - private fun generateFederatedOutputInstance(conn: UcGroupedConnection) = - "LF_FEDERATED_OUTPUT_CONNECTION_INSTANCE(${reactor.codeType}, ${conn.getUniqueName()});" + private fun generateFederatedConnectionCtor(conn: UcFederatedGroupedConnection) = + if (conn.srcFed == currentFederate) generateFederatedOutputCtor(conn) + else generateFederatedInputCtor(conn) - private fun generateFederatedInputInstance(conn: UcGroupedConnection) = - "LF_FEDERATED_INPUT_CONNECTION_INSTANCE(${reactor.codeType}, ${conn.getUniqueName()});" + private fun generateFederatedOutputInstance(conn: UcGroupedConnection) = + "LF_FEDERATED_OUTPUT_CONNECTION_INSTANCE(${reactor.codeType}, ${conn.getUniqueName()});" - private fun generateFederatedConnectionInstance(conn: UcFederatedGroupedConnection) = - if (conn.srcFed == currentFederate) generateFederatedOutputInstance(conn) else generateFederatedInputInstance( - conn - ) + private fun generateFederatedInputInstance(conn: UcGroupedConnection) = + "LF_FEDERATED_INPUT_CONNECTION_INSTANCE(${reactor.codeType}, ${conn.getUniqueName()});" - private fun generateInitializeFederatedOutput(conn: UcFederatedGroupedConnection) = - "LF_INITIALIZE_FEDERATED_OUTPUT_CONNECTION(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.serializeFunc});" + private fun generateFederatedConnectionInstance(conn: UcFederatedGroupedConnection) = + if (conn.srcFed == currentFederate) generateFederatedOutputInstance(conn) + else generateFederatedInputInstance(conn) - private fun generateInitializeFederatedInput(conn: UcFederatedGroupedConnection) = - "LF_INITIALIZE_FEDERATED_INPUT_CONNECTION(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.deserializeFunc});" + private fun generateInitializeFederatedOutput(conn: UcFederatedGroupedConnection) = + "LF_INITIALIZE_FEDERATED_OUTPUT_CONNECTION(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.serializeFunc});" - private fun generateInitializeFederatedConnection(conn: UcFederatedGroupedConnection) = - if (conn.srcFed == currentFederate) generateInitializeFederatedOutput(conn) else generateInitializeFederatedInput( - conn - ) + 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) = """ + private fun generateReactorCtorCode(conn: UcGroupedConnection) = + """ |${if (conn.isLogical) "LF_INITIALIZE_LOGICAL_CONNECTION(" else "LF_INITIALIZE_DELAYED_CONNECTION("}${reactor.codeType}, ${conn.getUniqueName()}, ${conn.bankWidth}, ${conn.portWidth}); - """.trimMargin() - - 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->${bundle.src.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->${bundle.dest.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) + """ + .trimMargin() + + 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->${bundle.src.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->${bundle.dest.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) } - - private fun generateFederatedConnectionBundleSelfStruct(bundle: UcFederatedConnectionBundle) = - with(PrependOperator) { - """ |typedef struct { + } + + 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) }} @@ -398,78 +417,78 @@ class UcConnectionGenerator( }); |} LF_FEDERATED_CONNECTION_BUNDLE_TYPE(${bundle.src.codeType}, ${bundle.dest.codeType}); | - """.trimMargin() - } + """ + .trimMargin() + } - private fun generateFederatedConnectionBundleCtor(bundle: UcFederatedConnectionBundle) = with(PrependOperator) { + 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() + """ + .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 + } } - - 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 - } - } + for (bundle in federatedConnectionBundles) { + for (conn in bundle.groupedConnections) { + if (conn.destFed == currentFederate) { + res += conn.maxNumPendingEvents } - return res + } } + return res + } - fun generateNetworkChannelIncludes(): String = - federatedConnectionBundles.distinctBy { it.networkChannel.type } - .joinWithLn { it.networkChannel.src.iface.includeHeaders } + fun generateNetworkChannelIncludes(): String = + federatedConnectionBundles + .distinctBy { it.networkChannel.type } + .joinWithLn { it.networkChannel.src.iface.includeHeaders } } 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 index f72878f5..037d8a5d 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionUtils.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionUtils.kt @@ -9,32 +9,37 @@ 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. +/** + * 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 = 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 - } - } + 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 = 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( @@ -42,14 +47,14 @@ open class UcGroupedConnection( 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. + 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 + private var uid: Int = -1 val bankWidth = srcInst?.codeWidth ?: 1 val portWidth = srcPort.width @@ -60,19 +65,19 @@ open class UcGroupedConnection( } val maxNumPendingEvents = if (getConnectionBufferSize(lfConn) > 0) getConnectionBufferSize(lfConn) else 1 - fun assignUid(id: Int) { - uid = id - } + fun assignUid(id: Int) { + uid = id + } - fun getUniqueName() = "conn_${srcInst?.name ?: ""}_${srcPort.name}_${uid}" + 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. + * 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, @@ -82,93 +87,98 @@ class UcFederatedGroupedConnection( 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" + // FIXME: Allow user to override and provide these. + val serializeFunc = "serialize_payload_default" + val deserializeFunc = "deserialize_payload_default" } /** - * A FederatedConnectionBundle will contain all GroupedConnections going between two federates, in either direction. - * It also contains a NetworkChannel connecting and NetworkEndpoint in each federate. + * 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 ) { - val networkChannel: UcNetworkChannel = UcNetworkChannel.createNetworkEndpointsAndChannelForBundle(this) + val networkChannel: UcNetworkChannel = + UcNetworkChannel.createNetworkEndpointsAndChannelForBundle(this) - fun numOutputs(federate: UcFederate) = groupedConnections.count { it.srcFed == federate } + fun numOutputs(federate: UcFederate) = groupedConnections.count { it.srcFed == federate } - fun numInputs(federate: UcFederate) = groupedConnections.count { it.destFed == federate } + fun numInputs(federate: UcFederate) = groupedConnections.count { it.destFed == federate } - fun generateNetworkChannelCtor(federate: UcFederate): String = - if (federate == src) { - networkChannel.generateChannelCtorSrc() - } else { - networkChannel.generateChannelCtorDest() - } + 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. + * 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 + 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()}]" + 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}" + fun generateChannelPointer() = "&self->${reactorInstance}${portInstance}" } /** - * This is a convenience-wrapper around a LF Port. It will construct - * UcChannels corresponding to the multiports and banks. + * 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. + * 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..() + + // 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) + fun takeRemainingChannels(): List = 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 + // 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 + 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 index 879c7f0e..7b2be9c0 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcExtensions.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcExtensions.kt @@ -4,25 +4,27 @@ 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) +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 - + get() = this.variable.name val TriggerRef.name: String - get() = when (this) { - is VarRef -> this.name + get() = + when (this) { + is VarRef -> this.name is BuiltinTriggerRef -> type.literal - else -> unreachable() - } + else -> unreachable() + } + +fun Attribute.getParamString(param: String): String? = + attrParms.find { it.name == param }?.value?.trim('"') -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.getParamInt(param: String): Int? = attrParms.find { it.name == param }?.value?.toInt() diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederate.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederate.kt index e729eb9c..45a4f8d6 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederate.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederate.kt @@ -5,29 +5,29 @@ import org.lflang.isBank import org.lflang.lf.Instantiation class UcFederate(val inst: Instantiation, val bankIdx: Int) { - val isBank = inst.isBank - private val interfaces = mutableListOf() + val isBank = inst.isBank + private val interfaces = mutableListOf() - val codeType = if (isBank) "${inst.codeTypeFederate}_${bankIdx}" else inst.codeTypeFederate + val codeType = if (isBank) "${inst.codeTypeFederate}_${bankIdx}" else inst.codeTypeFederate - fun addInterface(iface: UcNetworkInterface) { - interfaces.add(iface) - } + fun addInterface(iface: UcNetworkInterface) { + interfaces.add(iface) + } - fun getInterface(name: String): UcNetworkInterface = - interfaces.find { it.name == name }!! + fun getInterface(name: String): UcNetworkInterface { + return interfaces.find { it.name == name }!! + } - fun getDefaultInterface(): UcNetworkInterface = - interfaces.first() + fun getDefaultInterface(): UcNetworkInterface = interfaces.first() - fun getCompileDefs(): List = interfaces.distinctBy { it.type }.map { it.compileDefs } + fun getCompileDefs(): List = interfaces.distinctBy { it.type }.map { it.compileDefs } - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is UcFederate) return false + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is UcFederate) return false - val sameInst = inst == other.inst - val sameBank = bankIdx == other.bankIdx - return if (isBank) sameInst && sameBank else sameInst - } + val sameInst = inst == other.inst + val sameBank = bankIdx == other.bankIdx + return if (isBank) sameInst && sameBank else sameInst + } } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt index 6a4e097d..3f7764df 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt @@ -4,24 +4,31 @@ import org.lflang.* import org.lflang.generator.PrependOperator import org.lflang.lf.* +class UcFederateGenerator( + private val currentFederate: UcFederate, + private val otherFederates: List, + private val fileConfig: UcFileConfig, + messageReporter: MessageReporter +) { -class UcFederateGenerator(private val currentFederate: UcFederate, private val otherFederates: List, private val fileConfig: UcFileConfig, messageReporter: MessageReporter) { + private val container = currentFederate.inst.eContainer() as Reactor + private val reactor = currentFederate.inst.reactor + private val connections = UcConnectionGenerator(container, currentFederate, otherFederates) + private val parameters = UcParameterGenerator(container, currentFederate) + private val ports = UcPortGenerator(container, connections) + private val reactions = UcReactionGenerator(container) + private val instances = + UcInstanceGenerator( + container, parameters, ports, connections, reactions, fileConfig, messageReporter) + private val headerFile = "lf_federate.h" + private val includeGuard = "LFC_GEN_FEDERATE_${currentFederate.inst.name.uppercase()}_H" - private val container = currentFederate.inst.eContainer() as Reactor - private val reactor = currentFederate.inst.reactor - private val connections = UcConnectionGenerator(container, currentFederate, otherFederates) - private val parameters = UcParameterGenerator(container, currentFederate) - private val ports = UcPortGenerator(container, connections) - private val reactions = UcReactionGenerator(container) - private val instances = UcInstanceGenerator(container, parameters, ports, connections, reactions, fileConfig, messageReporter) - private val headerFile = "lf_federate.h" - private val includeGuard = "LFC_GEN_FEDERATE_${currentFederate.inst.name.uppercase()}_H" + fun getMaxNumPendingEvents(): Int { + return connections.getMaxNumPendingEvents() + } - fun getMaxNumPendingEvents(): Int { - return connections.getMaxNumPendingEvents() - } - - private fun generateFederateStruct() = with(PrependOperator) { + private fun generateFederateStruct() = + with(PrependOperator) { """ |typedef struct { | Reactor super; @@ -31,10 +38,12 @@ class UcFederateGenerator(private val currentFederate: UcFederate, private val o | LF_FEDERATE_BOOKKEEPING_INSTANCES(${connections.getNumFederatedConnectionBundles()}); |} ${currentFederate.codeType}; | - """.trimMargin() - } + """ + .trimMargin() + } - private fun generateCtorDefinition() = with(PrependOperator) { + private fun generateCtorDefinition() = + with(PrependOperator) { """ |${generateCtorDeclaration()} { | LF_FEDERATE_CTOR_PREAMBLE(); @@ -44,12 +53,14 @@ class UcFederateGenerator(private val currentFederate: UcFederate, private val o ${" | "..connections.generateReactorCtorCodes()} |} | - """.trimMargin() - } + """ + .trimMargin() + } - private fun generateCtorDeclaration() = "LF_REACTOR_CTOR_SIGNATURE(${currentFederate.codeType})" + private fun generateCtorDeclaration() = "LF_REACTOR_CTOR_SIGNATURE(${currentFederate.codeType})" - fun generateHeader() = with(PrependOperator) { + fun generateHeader() = + with(PrependOperator) { """ |#ifndef ${includeGuard} |#define ${includeGuard} @@ -62,10 +73,12 @@ class UcFederateGenerator(private val currentFederate: UcFederate, private val o ${" |"..generateFederateStruct()} ${" |"..generateCtorDeclaration()}; |#endif // ${includeGuard} - """.trimMargin() - } + """ + .trimMargin() + } - fun generateSource() = with(PrependOperator) { + fun generateSource() = + with(PrependOperator) { """ |#include "${headerFile}" | @@ -73,6 +86,7 @@ class UcFederateGenerator(private val currentFederate: UcFederate, private val o ${" |"..connections.generateCtors()} ${" |"..generateCtorDefinition()} | - """.trimMargin() - } + """ + .trimMargin() + } } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedLaunchScriptGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedLaunchScriptGenerator.kt index 6e1a71a4..31bc80cc 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedLaunchScriptGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedLaunchScriptGenerator.kt @@ -4,8 +4,10 @@ import org.lflang.generator.PrependOperator import org.lflang.joinWithLn class UcFederatedLaunchScriptGenerator(private val fileConfig: UcFileConfig) { - private val S = '$' // a little trick to escape the dollar sign with $S - fun generateLaunchScript(federates: List): String = with(PrependOperator) { + private val S = '$' // a little trick to escape the dollar sign with $S + + fun generateLaunchScript(federates: List): String = + with(PrependOperator) { """ |#!/bin/env bash | |set -m @@ -33,8 +35,9 @@ class UcFederatedLaunchScriptGenerator(private val fileConfig: UcFileConfig) { | wait $S{pid} || exit $S? |done |EXITED_SUCCESSFULLY=true - """.trimMargin() - } + """ + .trimMargin() + } private fun launchFederate(federate: UcFederate) = """ 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 index 39d5b38e..0d4ba2b4 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFileConfig.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFileConfig.kt @@ -1,37 +1,39 @@ 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.generator.uc.UcInstanceGenerator.Companion.codeTypeFederate -import org.lflang.lf.Instantiation import org.lflang.lf.Reactor import org.lflang.name import org.lflang.util.FileUtil -import java.io.IOException -import java.nio.file.Path class UcFileConfig(resource: Resource, srcGenBasePath: Path, useHierarchicalBin: Boolean) : FileConfig(resource, srcGenBasePath, useHierarchicalBin) { - @Throws(IOException::class) - override fun doClean() { - super.doClean() - this.ucBuildDirectories.forEach { FileUtil.deleteDirectory(it) } - } + @Throws(IOException::class) + override fun doClean() { + super.doClean() + this.ucBuildDirectories.forEach { FileUtil.deleteDirectory(it) } + } - val ucBuildDirectories = listOf( - this.outPath.resolve("build"), - ) + 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) + /** + * 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 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 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 index c306d128..ce9bb30f 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt @@ -1,5 +1,6 @@ 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 @@ -15,106 +16,110 @@ import org.lflang.reactor import org.lflang.scoping.LFGlobalScopeProvider import org.lflang.target.Target import org.lflang.target.property.* -import java.nio.file.Path /** 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) - } +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) + } + return UcGeneratorNonFederated(context, scopeProvider) } @Suppress("unused") abstract class UcGenerator( - val context: LFGeneratorContext, protected val scopeProvider: LFGlobalScopeProvider + val context: LFGeneratorContext, + protected val scopeProvider: LFGlobalScopeProvider ) : GeneratorBase(context) { - // keep a list of all source files we generate - val ucSources = mutableListOf() - val codeMaps = mutableMapOf() + // 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) + 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() + // 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) + // 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) + 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() + // 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" + } - 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 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() + } - // 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 + } - 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 getTarget() = Target.UC - override fun getTargetTypes(): TargetTypes = UcTypes + override fun getTargetTypes(): TargetTypes = UcTypes } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGeneratorFederated.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGeneratorFederated.kt index ab72af76..2be20744 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGeneratorFederated.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGeneratorFederated.kt @@ -1,5 +1,7 @@ package org.lflang.generator.uc +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 @@ -12,149 +14,142 @@ import org.lflang.reactor import org.lflang.scoping.LFGlobalScopeProvider import org.lflang.target.property.type.PlatformType import org.lflang.util.FileUtil -import java.nio.file.Path -import java.nio.file.Paths -class UcGeneratorFederated( - context: LFGeneratorContext, scopeProvider: LFGlobalScopeProvider -) : UcGenerator(context, scopeProvider) { - - private val nonFederatedGenerator = UcGeneratorNonFederated(context, scopeProvider) - val federates = mutableListOf() - - // Compute the total number of events and reactions within a federate. This reuses the - // computation for the non-federated case, but federated connections are also considered. - fun totalNumEventsAndReactionsFederated(federate: UcFederate): Pair { - val eventsFromFederatedConnections = maxNumPendingEvents[mainDef.reactor]!! - val eventsAndReactionsInFederate = nonFederatedGenerator.totalNumEventsAndReactions(federate.inst.reactor) - return Pair( - eventsFromFederatedConnections + eventsAndReactionsInFederate.first, - eventsAndReactionsInFederate.second - ) +class UcGeneratorFederated(context: LFGeneratorContext, scopeProvider: LFGlobalScopeProvider) : + UcGenerator(context, scopeProvider) { + + private val nonFederatedGenerator = UcGeneratorNonFederated(context, scopeProvider) + val federates = mutableListOf() + + // Compute the total number of events and reactions within a federate. This reuses the + // computation for the non-federated case, but federated connections are also considered. + fun totalNumEventsAndReactionsFederated(federate: UcFederate): Pair { + val eventsFromFederatedConnections = maxNumPendingEvents[mainDef.reactor]!! + val eventsAndReactionsInFederate = + nonFederatedGenerator.totalNumEventsAndReactions(federate.inst.reactor) + return Pair( + eventsFromFederatedConnections + eventsAndReactionsInFederate.first, + eventsAndReactionsInFederate.second) + } + + private fun doGenerateFederate( + resource: Resource, + context: LFGeneratorContext, + srcGenPath: Path, + federate: UcFederate + ): GeneratorResult.Status { + if (!canGenerate(errorsOccurred(), federate.inst, messageReporter, context)) + return GeneratorResult.Status.FAILED + + // generate header and source files for all reactors + getAllInstantiatedReactors(federate.inst.reactor).map { + nonFederatedGenerator.generateReactorFiles(it, srcGenPath) } - private fun doGenerateFederate( - resource: Resource, - context: LFGeneratorContext, - srcGenPath: Path, - federate: UcFederate - ): GeneratorResult.Status { - if (!canGenerate(errorsOccurred(), federate.inst, messageReporter, context)) return GeneratorResult.Status.FAILED - - // generate header and source files for all reactors - getAllInstantiatedReactors(federate.inst.reactor).map { - nonFederatedGenerator.generateReactorFiles( - it, - srcGenPath - ) - } - - generateFederateFiles(federate, srcGenPath) + generateFederateFiles(federate, srcGenPath) - // Collect the info on the generated sources - ucSources.addAll(nonFederatedGenerator.ucSources) - codeMaps.putAll(nonFederatedGenerator.codeMaps) + // Collect the info on the generated sources + ucSources.addAll(nonFederatedGenerator.ucSources) + codeMaps.putAll(nonFederatedGenerator.codeMaps) - 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 - } - - // The same UcGeneratorFederated is used to iteratively generated a project for each federate. - // After generating we need to clear certain state variables before starting with the next federate. - private fun clearStateFromPreviousFederate() { - codeMaps.clear() - ucSources.clear() - maxNumPendingEvents.clear() - nonFederatedGenerator.maxNumPendingEvents.clear() - nonFederatedGenerator.codeMaps.clear() - nonFederatedGenerator.ucSources.clear() + 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) } - - // This function creates an instantiation for the top-level main reactor. - private fun createMainDef() { - for (reactor in reactors) { - if (reactor.isFederated) { // Note that this will be the "main" top-level reactor. Not each federate. - this.mainDef = LfFactory.eINSTANCE.createInstantiation() - this.mainDef.name = reactor.name - this.mainDef.reactorClass = reactor - } - } + return GeneratorResult.Status.GENERATED + } + + // The same UcGeneratorFederated is used to iteratively generated a project for each federate. + // After generating we need to clear certain state variables before starting with the next + // federate. + private fun clearStateFromPreviousFederate() { + codeMaps.clear() + ucSources.clear() + maxNumPendingEvents.clear() + nonFederatedGenerator.maxNumPendingEvents.clear() + nonFederatedGenerator.codeMaps.clear() + nonFederatedGenerator.ucSources.clear() + } + + // This function creates an instantiation for the top-level main reactor. + private fun createMainDef() { + for (reactor in reactors) { + if (reactor + .isFederated) { // Note that this will be the "main" top-level reactor. Not each federate. + this.mainDef = LfFactory.eINSTANCE.createInstantiation() + this.mainDef.name = reactor.name + this.mainDef.reactorClass = reactor + } } - - override fun doGenerate(resource: Resource, context: LFGeneratorContext) { - super.doGenerate(resource, context) - createMainDef() - for (inst in getAllFederates()) { - for (bankIdx in 0.. { + val octets = address.address.split(".") + val lastOctet = octets.last().toInt() + require(lastOctet < (255 - count)) { + "Cannot increment the last octet of an IPv4 address beyond 255" + } + IPv4(octets.dropLast(1).joinToString(".") + "." + (lastOctet + count)) } - // Increment the last octet or segment of the IP - fun increment(address: IPAddress, count: Int): IPAddress { - return when (address) { - is IPv4 -> { - val octets = address.address.split(".") - val lastOctet = octets.last().toInt() - require(lastOctet < (255 - count)) { "Cannot increment the last octet of an IPv4 address beyond 255" } - IPv4(octets.dropLast(1).joinToString(".") + "." + (lastOctet + count)) - } - - is IPv6 -> { - val segments = address.address.split(":") - val lastSegment = BigInteger(segments.last(), 16) - val incremented = lastSegment + count.toBigInteger() - require( - incremented <= BigInteger( - "FFFF", - 16 - ) - ) { "Cannot increment the last segment of an IPv6 address beyond FFFF" } - IPv6(segments.dropLast(1).joinToString(":") + ":" + incremented.toString(16)) - } - } + is IPv6 -> { + val segments = address.address.split(":") + val lastSegment = BigInteger(segments.last(), 16) + val incremented = lastSegment + count.toBigInteger() + require(incremented <= BigInteger("FFFF", 16)) { + "Cannot increment the last segment of an IPv6 address beyond FFFF" + } + IPv6(segments.dropLast(1).joinToString(":") + ":" + incremented.toString(16)) } + } } + } } - - class IpPortManager { - private val currentPort = AtomicInteger(1024) // Starting port number - private val usedPorts = mutableSetOf() - - @Synchronized - fun acquirePortNumber(): Int { - while (true) { - val port = currentPort.getAndIncrement() - if (port in 1024..65535 && usedPorts.add(port)) { - return port - } - } - } - - @Synchronized - fun reservePortNumber(port: Int) { - assert(port < 65535) - assert(!usedPorts.contains(port)) - usedPorts.add(port) + private val currentPort = AtomicInteger(1024) // Starting port number + private val usedPorts = mutableSetOf() + + @Synchronized + fun acquirePortNumber(): Int { + while (true) { + val port = currentPort.getAndIncrement() + if (port in 1024..65535 && usedPorts.add(port)) { + return port + } } + } + + @Synchronized + fun reservePortNumber(port: Int) { + assert(port < 65535) + assert(!usedPorts.contains(port)) + usedPorts.add(port) + } } object IpAddressManager { - private val usedIps = mutableSetOf() - private val portManagers = mutableMapOf() - - @Synchronized - fun acquireIp(ip: IPAddress) { - if (ip != IPAddress.fromString("127.0.0.1")) { - require(!usedIps.contains(ip)) - usedIps.add(ip) - } + private val usedIps = mutableSetOf() + private val portManagers = mutableMapOf() + + @Synchronized + fun acquireIp(ip: IPAddress) { + if (ip != IPAddress.fromString("127.0.0.1")) { + require(!usedIps.contains(ip)) + usedIps.add(ip) } - - fun getPortManager(ip: IPAddress): IpPortManager { - if (portManagers.contains(ip)) { - return portManagers[ip]!! - } else { - val newManager = IpPortManager() - portManagers[ip] = newManager - return newManager - } + } + + fun getPortManager(ip: IPAddress): IpPortManager { + if (portManagers.contains(ip)) { + return portManagers[ip]!! + } else { + val newManager = IpPortManager() + portManagers[ip] = newManager + return newManager } + } } class UcIpAddress { } + 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 index 1312dd13..15a15f71 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt @@ -1,21 +1,29 @@ package org.lflang.generator.uc -import org.lflang.target.TargetConfig import org.lflang.generator.PrependOperator import org.lflang.generator.uc.UcReactorGenerator.Companion.codeType 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) { - abstract fun generateStartSource(): String - fun getDuration() = if (targetConfig.isSet(TimeOutProperty.INSTANCE)) targetConfig.get(TimeOutProperty.INSTANCE).toCCode() else "FOREVER" - fun keepAlive() = if(targetConfig.isSet(KeepaliveProperty.INSTANCE)) "true" else "false" - fun fast() = if(targetConfig.isSet(FastProperty.INSTANCE)) "true" else "false" - fun generateStartHeader() = with(PrependOperator) { + abstract fun generateStartSource(): String + + fun getDuration() = + if (targetConfig.isSet(TimeOutProperty.INSTANCE)) + targetConfig.get(TimeOutProperty.INSTANCE).toCCode() + else "FOREVER" + + fun keepAlive() = if (targetConfig.isSet(KeepaliveProperty.INSTANCE)) "true" else "false" + + fun fast() = if (targetConfig.isSet(FastProperty.INSTANCE)) "true" else "false" + + fun generateStartHeader() = + with(PrependOperator) { """ |#ifndef REACTOR_UC_LF_MAIN_H |#define REACTOR_UC_LF_MAIN_H @@ -24,28 +32,33 @@ abstract class UcMainGenerator(val targetConfig: TargetConfig) { | |#endif | - """.trimMargin() - } + """ + .trimMargin() + } - fun generateMainSource() = with(PrependOperator) { + fun generateMainSource() = + with(PrependOperator) { """ |#include "lf_start.h" |int main(void) { | lf_start(); | return 0; |} - """.trimMargin() - } + """ + .trimMargin() + } } + class UcMainGeneratorNonFederated( private val main: Reactor, targetConfig: TargetConfig, private val fileConfig: UcFileConfig, -): UcMainGenerator(targetConfig) { +) : UcMainGenerator(targetConfig) { - private val ucParameterGenerator = UcParameterGenerator(main) + private val ucParameterGenerator = UcParameterGenerator(main) - override fun generateStartSource() = with(PrependOperator) { + override fun generateStartSource() = + with(PrependOperator) { """ |#include "reactor-uc/reactor-uc.h" |#include "${fileConfig.getReactorHeaderPath(main).toUnixString()}" @@ -65,19 +78,23 @@ class UcMainGeneratorNonFederated( | lf_environment.start(&lf_environment); | lf_exit(); |} - """.trimMargin() - } + """ + .trimMargin() + } } + class UcMainGeneratorFederated( private val currentFederate: UcFederate, private val otherFederates: List, targetConfig: TargetConfig, private val fileConfig: UcFileConfig, -): UcMainGenerator(targetConfig) { +) : UcMainGenerator(targetConfig) { + + private val top = currentFederate.inst.eContainer() as Reactor + private val ucConnectionGenerator = UcConnectionGenerator(top, currentFederate, otherFederates) - private val top = currentFederate.inst.eContainer() as Reactor - private val ucConnectionGenerator = UcConnectionGenerator(top, currentFederate, otherFederates) - override fun generateStartSource() = with(PrependOperator) { + override fun generateStartSource() = + with(PrependOperator) { """ |#include "reactor-uc/reactor-uc.h" |#include "lf_federate.h" @@ -101,6 +118,7 @@ class UcMainGeneratorFederated( | lf_environment.start(&lf_environment); | lf_exit(); |} - """.trimMargin() - } + """ + .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 index 95509554..a6954c0c 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMakeGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMakeGenerator.kt @@ -1,19 +1,26 @@ package org.lflang.generator.uc +import java.nio.file.Path +import kotlin.io.path.name +import kotlin.math.max import org.lflang.FileConfig -import org.lflang.target.TargetConfig import org.lflang.generator.PrependOperator import org.lflang.joinWithLn import org.lflang.lf.Reactor +import org.lflang.target.TargetConfig import org.lflang.toUnixString -import java.nio.file.Path -import kotlin.io.path.name -import kotlin.math.max -abstract class UcMakeGenerator(private val mainTarget: String, private val numEvents: Int, private val numReactions: Int) { - 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) { +abstract class UcMakeGenerator( + private val mainTarget: String, + private val numEvents: Int, + private val numReactions: Int +) { + 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} @@ -25,17 +32,28 @@ abstract class UcMakeGenerator(private val mainTarget: String, private val numEv |REACTION_QUEUE_SIZE = ${max(numReactions, 1)} |EVENT_QUEUE_SIZE = ${max(numEvents, 2)} | - """.trimMargin() - } + """ + .trimMargin() + } } -class UcMakeGeneratorNonFederated(private val main: Reactor, private val targetConfig: TargetConfig, private val fileConfig: FileConfig, numEvents: Int, numReactions: Int) - : UcMakeGenerator(fileConfig.name, numEvents, numReactions) { - override fun generateMake(sources: List) = doGenerateMake(sources, emptyList()) - +class UcMakeGeneratorNonFederated( + private val main: Reactor, + private val targetConfig: TargetConfig, + private val fileConfig: FileConfig, + numEvents: Int, + numReactions: Int +) : UcMakeGenerator(fileConfig.name, numEvents, numReactions) { + override fun generateMake(sources: List) = doGenerateMake(sources, emptyList()) } -class UcMakeGeneratorFederated(private val federate: UcFederate, targetConfig: TargetConfig, fileConfig: UcFileConfig, numEvents: Int, numReactions: Int) - : UcMakeGenerator(federate.codeType, numEvents, numReactions) { - override fun generateMake(sources: List) = doGenerateMake(sources, federate.getCompileDefs()) +class UcMakeGeneratorFederated( + private val federate: UcFederate, + targetConfig: TargetConfig, + fileConfig: UcFileConfig, + numEvents: Int, + numReactions: Int +) : UcMakeGenerator(federate.codeType, numEvents, numReactions) { + override fun generateMake(sources: List) = + doGenerateMake(sources, federate.getCompileDefs()) } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt index dc21fa50..59e08399 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt @@ -7,147 +7,210 @@ import org.lflang.lf.Attribute // An enumeration of the supported NetworkChannels enum class NetworkChannelType { - TCP_IP, CUSTOM, COAP_UDP_IP, NONE + TCP_IP, + CUSTOM, + COAP_UDP_IP, + UART, + NONE } - object UcNetworkInterfaceFactory { - private val creators: Map UcNetworkInterface> = - mapOf( - Pair(TCP_IP) { federate, attr -> UcTcpIpInterface.fromAttribute(federate, attr) }, - Pair(COAP_UDP_IP) { federate, attr -> UcCoapUdpIpInterface.fromAttribute(federate, attr) }, - Pair(CUSTOM) { federate, attr -> UcCustomInterface.fromAttribute(federate, attr) } - ) - - fun createInterfaces(federate: UcFederate): List { - val attrs: List = getInterfaceAttributes(federate.inst) - return if (attrs.isEmpty()) { - listOf(createDefaultInterface()) - } else { - attrs.map { createInterfaceFromAttribute(federate, it) } - } + private val creators: + Map UcNetworkInterface> = + mapOf( + Pair(TCP_IP) { federate, attr -> UcTcpIpInterface.fromAttribute(federate, attr) }, + Pair(COAP_UDP_IP) { federate, attr -> + UcCoapUdpIpInterface.fromAttribute(federate, attr) + }, + Pair(CUSTOM) { federate, attr -> UcCustomInterface.fromAttribute(federate, attr) }, + Pair(UART) { federate, attr -> UcUARTInterface.fromAttribute(federate, attr) }) + + fun createInterfaces(federate: UcFederate): List { + val attrs: List = getInterfaceAttributes(federate.inst) + return if (attrs.isEmpty()) { + listOf(createDefaultInterface()) + } else { + attrs.map { createInterfaceFromAttribute(federate, it) } } - - private fun createInterfaceFromAttribute(federate: UcFederate, attr: Attribute): UcNetworkInterface { - val protocol = attr.attrName.substringAfter("_") - return when (protocol) { - "tcp" -> creators.get(TCP_IP)!!.invoke(federate, attr) - "coap" -> creators.get(COAP_UDP_IP)!!.invoke(federate, attr) - "custom" -> creators.get(CUSTOM)!!.invoke(federate, attr) - else -> throw IllegalArgumentException("Unrecognized interface attribute $attr") - } + } + + private fun createInterfaceFromAttribute( + federate: UcFederate, + attr: Attribute + ): UcNetworkInterface { + val protocol = attr.attrName.substringAfter("_") + return when (protocol) { + "tcp" -> creators.get(TCP_IP)!!.invoke(federate, attr) + "uart" -> creators.get(UART)!!.invoke(federate, attr) + "coap" -> creators.get(COAP_UDP_IP)!!.invoke(federate, attr) + "custom" -> creators.get(CUSTOM)!!.invoke(federate, attr) + else -> throw IllegalArgumentException("Unrecognized interface attribute $attr") } + } - private fun createDefaultInterface(): UcNetworkInterface = UcTcpIpInterface(ipAddress = IPAddress.fromString("127.0.0.1")) + private fun createDefaultInterface(): UcNetworkInterface = + UcTcpIpInterface(ipAddress = IPAddress.fromString("127.0.0.1")) } // A NetworkEndpoint is a communication endpoint located at the UcNetworkInterface of a federate. // A NetworkChannel is between two NetworkEndpoints. abstract class UcNetworkEndpoint(val iface: UcNetworkInterface) -class UcTcpIpEndpoint(val ipAddress: IPAddress, val port: Int, iface: UcTcpIpInterface) : UcNetworkEndpoint(iface) {} -class UcCoapUdpIpEndpoint(val ipAddress: IPAddress, iface: UcCoapUdpIpInterface) : UcNetworkEndpoint(iface) {} + +class UcTcpIpEndpoint(val ipAddress: IPAddress, val port: Int, iface: UcTcpIpInterface) : + UcNetworkEndpoint(iface) {} + +class UcUARTEndpoint( + val uart_device: Int, + val baud_rate: Int, + val data_bits: UARTDataBits, + val parity: UARTParityBits, + val stop_bits: UARTStopBits, + iface: UcUARTInterface +) : UcNetworkEndpoint(iface) {} + +class UcCoapUdpIpEndpoint(val ipAddress: IPAddress, iface: UcCoapUdpIpInterface) : + UcNetworkEndpoint(iface) {} + class UcCustomEndpoint(iface: UcCustomInterface) : UcNetworkEndpoint(iface) {} -// A federate can have several NetworkInterfaces, which are specified using attributes in the LF program. +// A federate can have several NetworkInterfaces, which are specified using attributes in the LF +// program. // A NetworkInterface has a name and can contain a set of endpoints. abstract class UcNetworkInterface(val type: NetworkChannelType, val name: String) { - val endpoints = mutableListOf() + val endpoints = mutableListOf() - /** A header file that should be included to support this NetworkInterface. Used by CustomInterface */ - abstract val includeHeaders: String + /** + * A header file that should be included to support this NetworkInterface. Used by CustomInterface + */ + abstract val includeHeaders: String - /** A compile definition which must be defined to get support for this NetworkInterface*/ - abstract val compileDefs: String + /** A compile definition which must be defined to get support for this NetworkInterface */ + abstract val compileDefs: String } class UcTcpIpInterface(private val ipAddress: IPAddress, name: String? = null) : UcNetworkInterface(TCP_IP, name ?: "tcp") { - private val portManager = IpAddressManager.getPortManager(ipAddress) - override val includeHeaders: String = "" - override val compileDefs: String = "NETWORK_CHANNEL_TCP_POSIX" - - fun createEndpoint(port: Int?): UcTcpIpEndpoint { - val portNum = if (port != null) { - portManager.reservePortNumber(port) - port + private val portManager = IpAddressManager.getPortManager(ipAddress) + override val includeHeaders: String = "" + override val compileDefs: String = "NETWORK_CHANNEL_TCP_POSIX" + + fun createEndpoint(port: Int?): UcTcpIpEndpoint { + val portNum = + if (port != null) { + portManager.reservePortNumber(port) + port } else { - portManager.acquirePortNumber() + portManager.acquirePortNumber() } - val ep = UcTcpIpEndpoint(ipAddress, portNum, this) - endpoints.add(ep) - return ep + val ep = UcTcpIpEndpoint(ipAddress, portNum, this) + endpoints.add(ep) + return ep + } + + companion object { + fun fromAttribute(federate: UcFederate, attr: Attribute): UcTcpIpInterface { + val address = attr.getParamString("address") + val name = attr.getParamString("name") + val ip = + if (address != null) { + var address = IPAddress.fromString(address) + + if (federate.isBank) { + address = IPAddress.increment(address, federate.bankIdx - 1) + } + address + } else { + IPAddress.fromString("127.0.0.1") + } + IpAddressManager.acquireIp(ip) + return UcTcpIpInterface(ip, name) } + } +} - companion object { - fun fromAttribute(federate: UcFederate, attr: Attribute): UcTcpIpInterface { - val address = attr.getParamString("address") - val name = attr.getParamString("name") - val ip = if (address != null) { - var address = IPAddress.fromString(address) - - if (federate.isBank) { - address = IPAddress.increment(address, federate.bankIdx - 1) - } - address - } else { - IPAddress.fromString("127.0.0.1") - } - IpAddressManager.acquireIp(ip) - return UcTcpIpInterface(ip, name) - } +class UcUARTInterface( + private val uartDevice: Int, + private val baudRate: Int, + private val dataBits: UARTDataBits, + private val parity: UARTParityBits, + private val stopBits: UARTStopBits, + name: String? = null +) : UcNetworkInterface(UART, name ?: "uart") { + + override val includeHeaders: String = "" + override val compileDefs: String = "NETWORK_CHANNEL_UART" + + fun createEndpoint(): UcUARTEndpoint { + val ep = UcUARTEndpoint(uartDevice, baudRate, dataBits, parity, stopBits, this) + endpoints.add(ep) + return ep + } + + companion object { + fun fromAttribute(federate: UcFederate, attr: Attribute): UcUARTInterface { + val uartDevice = attr.getParamString("uart_device")?.toInt() ?: 0 + val baudRate = attr.getParamString("baud_rate")?.toInt() ?: 9600 + val dataBits = UARTDataBits.valueOf(attr.getParamString("data_bits").toString()) + val parity = UARTParityBits.valueOf(attr.getParamString("parity").toString()) + val uartStopBits = UARTStopBits.valueOf(attr.getParamString("stop_bits").toString()) + val name = attr.getParamString("name") + UARTDeviceManager.reserve(uartDevice) + return UcUARTInterface(uartDevice, baudRate, dataBits, parity, uartStopBits, name) } + } } class UcCoapUdpIpInterface(private val ipAddress: IPAddress, name: String? = null) : UcNetworkInterface(COAP_UDP_IP, name ?: "coap") { - override val includeHeaders: String = "" - override val compileDefs: String = "NETWORK_CHANNEL_COAP" - - fun createEndpoint(): UcCoapUdpIpEndpoint { - val ep = UcCoapUdpIpEndpoint(ipAddress, this) - endpoints.add(ep) - return ep - } - - companion object { - fun fromAttribute(federate: UcFederate, attr: Attribute): UcCoapUdpIpInterface { - val address = attr.getParamString("address") - val name = attr.getParamString("name") - val ip = if (address != null) { - var address = IPAddress.fromString(address) - - if (federate.isBank) { - address = IPAddress.increment(address, federate.bankIdx - 1) - } - address - } else { - IPAddress.fromString("127.0.0.1") + override val includeHeaders: String = "" + override val compileDefs: String = "NETWORK_CHANNEL_COAP" + + fun createEndpoint(): UcCoapUdpIpEndpoint { + val ep = UcCoapUdpIpEndpoint(ipAddress, this) + endpoints.add(ep) + return ep + } + + companion object { + fun fromAttribute(federate: UcFederate, attr: Attribute): UcCoapUdpIpInterface { + val address = attr.getParamString("address") + val name = attr.getParamString("name") + val ip = + if (address != null) { + var address = IPAddress.fromString(address) + + if (federate.isBank) { + address = IPAddress.increment(address, federate.bankIdx - 1) } - IpAddressManager.acquireIp(ip) - return UcCoapUdpIpInterface(ip, name) - } + address + } else { + IPAddress.fromString("127.0.0.1") + } + IpAddressManager.acquireIp(ip) + return UcCoapUdpIpInterface(ip, name) } + } } class UcCustomInterface(name: String, val include: String, val args: String? = null) : UcNetworkInterface(CUSTOM, name) { - override val compileDefs = "" - override val includeHeaders: String = "#include \"$include\"" - - fun createEndpoint(): UcCustomEndpoint { - val ep = UcCustomEndpoint(this) - endpoints.add(ep) - return ep - } - - companion object { - fun fromAttribute(federate: UcFederate, attr: Attribute): UcCustomInterface { - val name = attr.getParamString("name") - val include = attr.getParamString("include") - val args = attr.getParamString("args") - return UcCustomInterface(name!!, include!!, args) - } + override val compileDefs = "" + override val includeHeaders: String = "#include \"$include\"" + + fun createEndpoint(): UcCustomEndpoint { + val ep = UcCustomEndpoint(this) + endpoints.add(ep) + return ep + } + + companion object { + fun fromAttribute(federate: UcFederate, attr: Attribute): UcCustomInterface { + val name = attr.getParamString("name") + val include = attr.getParamString("include") + val args = attr.getParamString("args") + return UcCustomInterface(name!!, include!!, args) } + } } /** A UcNetworkChannel is created by giving two endpoints and deciding which one is the server */ @@ -157,68 +220,83 @@ abstract class UcNetworkChannel( val dest: UcNetworkEndpoint, val serverLhs: Boolean, ) { - /** Generate code calling the constructor of the source endpoint */ - abstract fun generateChannelCtorSrc(): String - /** Generate code calling the constructor of the destination endpoint */ - abstract fun generateChannelCtorDest(): String - abstract val codeType: String - - companion object { - /** Given a FederatedConnection bundle which contains an LF connection and - * all the connection channels. Create an endpoint at source and destination and a UcNetworkChannel - * connecting the, - */ - fun createNetworkEndpointsAndChannelForBundle(bundle: UcFederatedConnectionBundle): UcNetworkChannel { - val attr: Attribute? = getLinkAttribute(bundle.groupedConnections.first().lfConn) - var srcIf: UcNetworkInterface - var destIf: UcNetworkInterface - var channel: UcNetworkChannel - var serverLhs = true - var serverPort: Int? = null - - if (attr == null) { - // If there is no @link attribute on the connection we just get the default (unless there - // is ambiguity) - srcIf = bundle.src.getDefaultInterface() - destIf = bundle.dest.getDefaultInterface() - } else { - // Parse the @link attribute and generate a UcNetworkChannel between the correct - // interfaces. - val srcIfName = attr.getParamString("left") - val destIfName = attr.getParamString("right") - val serverSideAttr = attr.getParamString("server_side") - serverPort = attr.getParamInt("server_port") - srcIf = - if (srcIfName != null) bundle.src.getInterface(srcIfName) else bundle.src.getDefaultInterface() - destIf = - if (destIfName != null) bundle.dest.getInterface(destIfName) else bundle.dest.getDefaultInterface() - serverLhs = if (serverSideAttr == null) true else !serverSideAttr!!.equals("right") - } + /** Generate code calling the constructor of the source endpoint */ + abstract fun generateChannelCtorSrc(): String + + /** Generate code calling the constructor of the destination endpoint */ + abstract fun generateChannelCtorDest(): String + + abstract val codeType: String + + companion object { + /** + * Given a FederatedConnection bundle which contains an LF connection and all the connection + * channels. Create an endpoint at source and destination and a UcNetworkChannel connecting the, + */ + fun createNetworkEndpointsAndChannelForBundle( + bundle: UcFederatedConnectionBundle + ): UcNetworkChannel { + val attr: Attribute? = getLinkAttribute(bundle.groupedConnections.first().lfConn) + var srcIf: UcNetworkInterface + var destIf: UcNetworkInterface + var channel: UcNetworkChannel + var serverLhs = true + var serverPort: Int? = null + + if (attr == null) { + // If there is no @link attribute on the connection we just get the default (unless there + // is ambiguity) + srcIf = bundle.src.getDefaultInterface() + destIf = bundle.dest.getDefaultInterface() + } else { + // Parse the @link attribute and generate a UcNetworkChannel between the correct + // interfaces. + val srcIfName = attr.getParamString("left") + val destIfName = attr.getParamString("right") + val serverSideAttr = attr.getParamString("server_side") + serverPort = attr.getParamInt("server_port") + srcIf = + if (srcIfName != null) bundle.src.getInterface(srcIfName) + else bundle.src.getDefaultInterface() + destIf = + if (destIfName != null) bundle.dest.getInterface(destIfName) + else bundle.dest.getDefaultInterface() + serverLhs = if (serverSideAttr == null) true else !serverSideAttr!!.equals("right") + } + + require(srcIf.type == destIf.type) + when (srcIf.type) { + TCP_IP -> { + val srcEp = + (srcIf as UcTcpIpInterface).createEndpoint(if (serverLhs) serverPort else null) + val destEp = + (destIf as UcTcpIpInterface).createEndpoint(if (!serverLhs) serverPort else null) + channel = UcTcpIpChannel(srcEp, destEp, serverLhs) + } - require(srcIf.type == destIf.type) - when (srcIf.type) { - TCP_IP -> { - val srcEp = (srcIf as UcTcpIpInterface).createEndpoint(if (serverLhs) serverPort else null) - val destEp = (destIf as UcTcpIpInterface).createEndpoint(if (!serverLhs) serverPort else null) - channel = UcTcpIpChannel(srcEp, destEp, serverLhs) - } - - COAP_UDP_IP -> { - val srcEp = (srcIf as UcCoapUdpIpInterface).createEndpoint() - val destEp = (destIf as UcCoapUdpIpInterface).createEndpoint() - channel = UcCoapUdpIpChannel(srcEp, destEp) - } - - CUSTOM -> { - val srcEp = (srcIf as UcCustomInterface).createEndpoint() - val destEp = (destIf as UcCustomInterface).createEndpoint() - channel = UcCustomChannel(srcEp, destEp) - } - NONE -> throw IllegalArgumentException("Tried creating network channel with type=NONE") - } - return channel + UART -> { + val srcEp = (srcIf as UcUARTInterface).createEndpoint() + val destEp = (srcIf as UcUARTInterface).createEndpoint() + channel = UcUARTChannel(srcEp, destEp) + } + + COAP_UDP_IP -> { + val srcEp = (srcIf as UcCoapUdpIpInterface).createEndpoint() + val destEp = (destIf as UcCoapUdpIpInterface).createEndpoint() + channel = UcCoapUdpIpChannel(srcEp, destEp) } + + CUSTOM -> { + val srcEp = (srcIf as UcCustomInterface).createEndpoint() + val destEp = (destIf as UcCustomInterface).createEndpoint() + channel = UcCustomChannel(srcEp, destEp) + } + + NONE -> throw IllegalArgumentException("Tried creating network channel with type=NONE") + } + return channel } + } } class UcTcpIpChannel( @@ -226,17 +304,30 @@ class UcTcpIpChannel( dest: UcTcpIpEndpoint, serverLhs: Boolean = true, ) : UcNetworkChannel(TCP_IP, src, dest, serverLhs) { - private val srcTcp = src - private val destTcp = dest + private val srcTcp = src + private val destTcp = dest + + override fun generateChannelCtorSrc() = + "TcpIpChannel_ctor(&self->channel, \"${if (serverLhs) srcTcp.ipAddress.address else destTcp.ipAddress.address}\", ${if (serverLhs) srcTcp.port else destTcp.port}, AF_INET, ${serverLhs});" + + override fun generateChannelCtorDest() = + "TcpIpChannel_ctor(&self->channel, \"${if (serverLhs) srcTcp.ipAddress.address else destTcp.ipAddress.address}\", ${if (serverLhs) srcTcp.port else destTcp.port}, AF_INET, ${!serverLhs});" - override fun generateChannelCtorSrc() = - "TcpIpChannel_ctor(&self->channel, \"${if (serverLhs) srcTcp.ipAddress.address else destTcp.ipAddress.address}\", ${if (serverLhs) srcTcp.port else destTcp.port}, AF_INET, ${serverLhs});" + override val codeType: String + get() = "TcpIpChannel" +} + +class UcUARTChannel(private val uart_src: UcUARTEndpoint, private val uart_dest: UcUARTEndpoint) : + UcNetworkChannel(UART, uart_src, uart_dest, false) { - override fun generateChannelCtorDest() = - "TcpIpChannel_ctor(&self->channel, \"${if (serverLhs) srcTcp.ipAddress.address else destTcp.ipAddress.address}\", ${if (serverLhs) srcTcp.port else destTcp.port}, AF_INET, ${!serverLhs});" + override fun generateChannelCtorSrc() = + "UARTChannel_ctor(&self->channel, &env, ${uart_src.uart_device}, ${uart_src.baud_rate}, UC_${uart_src.data_bits}, UC_${uart_src.parity}, UC_${uart_src.stop_bits});" - override val codeType: String - get() = "TcpIpChannel" + override fun generateChannelCtorDest() = + "UARTChannel_ctor(&self->channel, &env, ${uart_dest.uart_device}, ${uart_dest.baud_rate}, UC_${uart_dest.data_bits}, UC_${uart_dest.parity}, UC_${uart_dest.stop_bits});" + + override val codeType: String + get() = uart_dest.iface.name } class UcCoapUdpIpChannel( @@ -244,25 +335,25 @@ class UcCoapUdpIpChannel( dest: UcCoapUdpIpEndpoint, // TODO: In CoAP every node is a server and a client => default server to false for now ) : UcNetworkChannel(COAP_UDP_IP, src, dest, false) { - private val srcAddr = src - private val destAddr = dest - - private fun getIpProtocolFamily(ip: IPAddress): String { - return when (ip) { - is IPAddress.IPv4 -> "AF_INET" - is IPAddress.IPv6 -> "AF_INET6" - else -> throw IllegalArgumentException("Unknown IP address type") - } + private val srcAddr = src + private val destAddr = dest + + private fun getIpProtocolFamily(ip: IPAddress): String { + return when (ip) { + is IPAddress.IPv4 -> "AF_INET" + is IPAddress.IPv6 -> "AF_INET6" + else -> throw IllegalArgumentException("Unknown IP address type") } + } - override fun generateChannelCtorSrc() = - "CoapUdpIpChannel_ctor(&self->channel, \"${destAddr.ipAddress.address}\", ${getIpProtocolFamily(destAddr.ipAddress)});" + override fun generateChannelCtorSrc() = + "CoapUdpIpChannel_ctor(&self->channel, \"${destAddr.ipAddress.address}\", ${getIpProtocolFamily(destAddr.ipAddress)});" - override fun generateChannelCtorDest() = - "CoapUdpIpChannel_ctor(&self->channel, \"${srcAddr.ipAddress.address}\", ${getIpProtocolFamily(srcAddr.ipAddress)});" + override fun generateChannelCtorDest() = + "CoapUdpIpChannel_ctor(&self->channel, \"${srcAddr.ipAddress.address}\", ${getIpProtocolFamily(srcAddr.ipAddress)});" - override val codeType: String - get() = "CoapUdpIpChannel" + override val codeType: String + get() = "CoapUdpIpChannel" } class UcCustomChannel( @@ -270,17 +361,15 @@ class UcCustomChannel( dest: UcCustomEndpoint, serverLhs: Boolean = true, ) : UcNetworkChannel(CUSTOM, src, dest, serverLhs) { - val srcIface = src.iface as UcCustomInterface - val destIface = dest.iface as UcCustomInterface - private val srcArgs = if (srcIface.args != null) ", ${srcIface.args}" else "" - private val destArgs = if (destIface.args != null) ", ${destIface.args}" else "" + val srcIface = src.iface as UcCustomInterface + val destIface = dest.iface as UcCustomInterface + private val srcArgs = if (srcIface.args != null) ", ${srcIface.args}" else "" + private val destArgs = if (destIface.args != null) ", ${destIface.args}" else "" - override fun generateChannelCtorSrc() = - "${srcIface.name}_ctor(&self->channel, ${srcArgs});" + override fun generateChannelCtorSrc() = "${srcIface.name}_ctor(&self->channel, ${srcArgs});" - override fun generateChannelCtorDest() = - "${destIface.name}_ctor(&self->channel, ${destArgs});" + override fun generateChannelCtorDest() = "${destIface.name}_ctor(&self->channel, ${destArgs});" - override val codeType: String - get() = srcIface.name + override val codeType: String + get() = srcIface.name } 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 index f11ee7b2..0c3c699c 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcParameterGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcParameterGenerator.kt @@ -7,41 +7,48 @@ import org.lflang.lf.Reactor class UcParameterGenerator(private val reactor: Reactor, private val federate: UcFederate? = null) { - companion object { + companion object { - val Parameter.targetType get(): String = this.inferredType.CType + val Parameter.targetType + get(): String = this.inferredType.CType - val Parameter.isPresentName get(): String = "__${this.name}" - } + 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 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") { - """| + 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()}" - } - } + """ + .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 index defa85bc..49895e29 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGenerator.kt @@ -1,207 +1,213 @@ package org.lflang.generator.uc +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.Paths +import kotlin.io.path.createSymbolicLinkPointingTo import org.lflang.MessageReporter import org.lflang.generator.CodeMap -import org.lflang.target.TargetConfig import org.lflang.generator.GeneratorCommandFactory import org.lflang.generator.LFGeneratorContext -import org.lflang.generator.uc.UcInstanceGenerator.Companion.isAFederate -import org.lflang.reactor +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 -import java.nio.file.Files -import java.nio.file.Path -import java.nio.file.Paths -import kotlin.io.path.createSymbolicLinkPointingTo /** 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( + 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() + companion object { + fun buildTypeToCmakeConfig(type: BuildType) = + when (type) { + BuildType.TEST -> "Debug" + else -> type.toString() } + } + + 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 } - - 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.generateCmake(ucSources), srcGenPath.resolve("CMakeLists.txt"), true) - val runtimeSymlinkPath: Path = srcGenPath.resolve("reactor-uc"); - try { - runtimeSymlinkPath.createSymbolicLinkPointingTo(runtimePath); - } catch (e: Exception) { - // Do nothing - } - - FileUtil.writeToFile(makeGenerator.generateMake(ucSources), srcGenPath.resolve("Makefile"), true) + 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.generateCmake(ucSources), srcGenPath.resolve("CMakeLists.txt"), true) + val runtimeSymlinkPath: Path = srcGenPath.resolve("reactor-uc") + try { + runtimeSymlinkPath.createSymbolicLinkPointingTo(runtimePath) + } catch (e: Exception) { + // Do nothing } - 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 + 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 } - 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) { + 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 (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 + 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") } - - return version + } + if (cmakeReturnCode != 0) { + messageReporter.nowhere().error("cmake failed with error code $cmakeReturnCode") + } } - private fun runCmake(context: LFGeneratorContext): Int { - val cmakeCommand = createCmakeCommand(buildPath, fileConfig.outPath) - return cmakeCommand.run(context.cancelIndicator) + 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 } - - 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 + 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 } - 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 - ) + return version + } - if (parallelize) { - makeArgs.addAll(listOf("--parallel", Runtime.getRuntime().availableProcessors().toString())) - } + private fun runCmake(context: LFGeneratorContext): Int { + val cmakeCommand = createCmakeCommand(buildPath, fileConfig.outPath) + return cmakeCommand.run(context.cancelIndicator) + } - 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 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 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 + 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/UcPlatformGeneratorFederated.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorFederated.kt index 69c579e2..6bef831d 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorFederated.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorFederated.kt @@ -1,29 +1,45 @@ package org.lflang.generator.uc -import org.lflang.reactor +import java.nio.file.Path import org.lflang.target.property.PlatformProperty import org.lflang.target.property.type.PlatformType import org.lflang.util.FileUtil -import java.nio.file.Path -class UcPlatformGeneratorFederated(generator: UcGeneratorFederated, override val srcGenPath: Path, private val federate: UcFederate) : - UcPlatformGenerator(generator) { +class UcPlatformGeneratorFederated( + generator: UcGeneratorFederated, + override val srcGenPath: Path, + private val federate: UcFederate +) : UcPlatformGenerator(generator) { - override val buildPath = srcGenPath.resolve("build") - override val targetName: String = federate.codeType + override val buildPath = srcGenPath.resolve("build") + override val targetName: String = federate.codeType - override fun generatePlatformFiles() { - val generator = generator as UcGeneratorFederated - val mainGenerator = UcMainGeneratorFederated(federate, generator.federates, generator.targetConfig, generator.fileConfig) - val numEventsAndReactions = generator.totalNumEventsAndReactionsFederated(federate) - val cmakeGenerator = UcCmakeGeneratorFederated(federate, targetConfig, generator.fileConfig, numEventsAndReactions.first, numEventsAndReactions.second) - val makeGenerator = UcMakeGeneratorFederated(federate, targetConfig, generator.fileConfig, numEventsAndReactions.first, numEventsAndReactions.second) - super.doGeneratePlatformFiles(mainGenerator, cmakeGenerator, makeGenerator) + override fun generatePlatformFiles() { + val generator = generator as UcGeneratorFederated + val mainGenerator = + UcMainGeneratorFederated( + federate, generator.federates, generator.targetConfig, generator.fileConfig) + val numEventsAndReactions = generator.totalNumEventsAndReactionsFederated(federate) + val cmakeGenerator = + UcCmakeGeneratorFederated( + federate, + targetConfig, + generator.fileConfig, + numEventsAndReactions.first, + numEventsAndReactions.second) + val makeGenerator = + UcMakeGeneratorFederated( + federate, + targetConfig, + generator.fileConfig, + numEventsAndReactions.first, + numEventsAndReactions.second) + super.doGeneratePlatformFiles(mainGenerator, cmakeGenerator, makeGenerator) - if (targetConfig.get(PlatformProperty.INSTANCE).platform == PlatformType.Platform.NATIVE) { - generateLaunchScript() - } + if (targetConfig.get(PlatformProperty.INSTANCE).platform == PlatformType.Platform.NATIVE) { + generateLaunchScript() } + } fun generateLaunchScript() { val generator = generator as UcGeneratorFederated 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 index 6e750797..564ab945 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorNonFederated.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorNonFederated.kt @@ -1,23 +1,13 @@ package org.lflang.generator.uc -import org.lflang.generator.CodeMap -import org.lflang.generator.LFGeneratorContext -import org.lflang.reactor -import org.lflang.target.property.BuildTypeProperty -import org.lflang.target.property.type.BuildTypeType.BuildType -import org.lflang.toUnixString -import org.lflang.util.FileUtil -import org.lflang.util.LFCommand -import java.nio.file.Files import java.nio.file.Path -import java.nio.file.Paths -import kotlin.io.path.createSymbolicLinkPointingTo +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 val buildPath = srcGenPath.resolve("build") + override val targetName = fileConfig.name override fun generatePlatformFiles() { val mainGenerator = UcMainGeneratorNonFederated(mainReactor, generator.targetConfig, generator.fileConfig) 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 index ceece6fc..a1663bb6 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt @@ -1,27 +1,25 @@ -/************* +/** + * ********** * 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. - ***************/ - + * + * 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.* @@ -32,83 +30,111 @@ 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" - - companion object { - val Port.width - get(): Int = widthSpec?.getWidth()?:1 - val Type.isArray - get(): Boolean = cStyleArraySpec != null - val Type.arrayLength - get(): Int = cStyleArraySpec.length - +class UcPortGenerator( + private val reactor: Reactor, + private val connections: UcConnectionGenerator +) { + val Port.external_args + get(): String = "_${name}_external" + + 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 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});" + 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)});" - 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 -> "" - } + 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()});" } - - fun generateReactorStructFields() = reactor.allInputs.plus(reactor.allOutputs).joinToString(prefix = "// Ports \n", separator = "\n", postfix = "\n") { + } + + 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]"} + } + + 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/UcPreambleGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPreambleGenerator.kt index 366c4183..a2060cbe 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPreambleGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPreambleGenerator.kt @@ -6,7 +6,6 @@ import org.lflang.* import org.lflang.generator.PrependOperator import org.lflang.generator.PrependOperator.rangeTo import org.lflang.lf.Preamble -import org.lflang.lf.Reactor import org.lflang.scoping.LFGlobalScopeProvider class UcPreambleGenerator( @@ -14,17 +13,17 @@ class UcPreambleGenerator( 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 includeGuard = "LF_GEN_${resource.name.uppercase()}_PREAMBLE_H" + /** A list of all preambles defined in the resource (file) */ + private val preambles: EList = resource.model.preambles + private val includeGuard = "LF_GEN_${resource.name.uppercase()}_PREAMBLE_H" - fun generateHeader(): String { - val importedResources = scopeProvider.getImportedResources(resource) - val includes = importedResources.map { """#include "${fileConfig.getPreambleHeaderPath(it)}"""" } + fun generateHeader(): String { + val importedResources = scopeProvider.getImportedResources(resource) + val includes = + importedResources.map { """#include "${fileConfig.getPreambleHeaderPath(it)}"""" } - - return with(PrependOperator) { - """ + return with(PrependOperator) { + """ |#ifndef ${includeGuard} |#define ${includeGuard} | @@ -33,7 +32,8 @@ class UcPreambleGenerator( | ${" |"..preambles.joinToString(separator = "\n") { it.code.toText() }} |#endif // ${includeGuard} - """.trimMargin() - } + """ + .trimMargin() } + } } 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 index 25f1f172..9d9c6dc7 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactionGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactionGenerator.kt @@ -9,267 +9,293 @@ 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.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.ctorDeadlineArg + get() = if (deadline != null) ", ${deadline.delay.toCCode()}" else "" + private val Reaction.allUncontainedTriggers + get() = triggers.filterNot { it.isEffectOf(this) || it.isContainedRef } - private val Reaction.ctorDeadlineArg - get() = if (deadline != null) ", ${deadline.delay.toCCode()}" else "" - - private val Reaction.allUncontainedTriggers - get() = triggers.filterNot { it.isEffectOf(this) || it.isContainedRef } - 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 Reaction.allContainedTriggers - get() = triggers.filter { !it.isEffectOf(this) && it.isContainedRef } - private val Reaction.allContainedSources - get() = sources.filter { !it.isEffectOf(this) && it.isContainedRef } - - private val reactionsWithDeadline = reactor.allReactions.filter {it.deadline != null} - - // 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 - if (variable is Port) { - res += variable.width - } else { - res += 1 - } - } - for (effect in allContainedEffects) { - res += effect.container.codeWidth * (effect.variable as Port).width - } - return res - } + private val Reaction.allUncontainedEffects + get() = effects.filterNot { it.isContainedRef } - 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 val Reaction.allUncontainedSources + get() = sources.filterNot { it.isContainedRef } - private fun TriggerRef.isEffectOf(reaction: Reaction): Boolean = this is VarRef && isEffectOf(reaction) + private val Reaction.allContainedEffects + get() = effects.filter { it.isContainedRef } - 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 Reaction.allContainedTriggers + get() = triggers.filter { !it.isEffectOf(this) && it.isContainedRef } - 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.fullName: String get() = if (container != null) "${container.name}.${name}" else name + private val Reaction.allContainedSources + get() = sources.filter { !it.isEffectOf(this) && it.isContainedRef } - private val VarRef.isContainedRef: Boolean get() = container != null - private val TriggerRef.isContainedRef: Boolean get() = this is VarRef && isContainedRef + private val reactionsWithDeadline = reactor.allReactions.filter { it.deadline != null } - 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.codeWidth-1)).toList().joinToString(separator = "\n") { - "LF_PORT_REGISTER_SOURCE(self->${varRef.container.name}[${it}].${port.name}, ${reaction.nameInReactor}, ${port.width})" - } + // 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 + if (variable is Port) { + res += variable.width } else { - "LF_PORT_REGISTER_SOURCE(self->${varRef.name}, ${reaction.nameInReactor}, ${port.width});" + res += 1 } + } + for (effect in allContainedEffects) { + res += effect.container.codeWidth * (effect.variable as Port).width + } + return res + } - 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 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 fun registerEffect(triggerRef: TriggerRef, reaction: Reaction) = + private val TriggerRef.scope + get() = 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") + 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 fun registerPortEffect(varRef: VarRef, port: Port, reaction: Reaction) = - if (varRef.container != null) { - (0..(varRef.container.codeWidth-1)).toList().joinToString(separator = "\n") { - "LF_PORT_REGISTER_EFFECT(self->${varRef.container.name}[${it}].${port.name}, ${reaction.nameInReactor}, ${port.width})" + 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 { - "LF_PORT_REGISTER_EFFECT(self->${varRef.name}, ${reaction.nameInReactor}, ${port.width});" + } + else -> throw AssertionError("Unexpected variable type") } - 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 val VarRef.fullName: String + get() = if (container != null) "${container.name}.${name}" else name - private fun registerPortObserver(varRef: VarRef, port: Port, reaction: Reaction) = - if (varRef.container != null) { - (0..(varRef.container.codeWidth-1)).toList().joinToString(separator = "\n") { - "LF_PORT_REGISTER_OBSERVER(self->${varRef.container.name}[${it}].${port.name}, ${reaction.nameInReactor}, ${port.width})" - } - } else { - "LF_PORT_REGISTER_OBSERVER(self->${varRef.name}, ${reaction.nameInReactor}, ${port.width});" + 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.codeWidth - 1)).toList().joinToString(separator = "\n") { + "LF_PORT_REGISTER_SOURCE(self->${varRef.container.name}[${it}].${port.name}, ${reaction.nameInReactor}, ${port.width})" } - private fun registerObserver(varRef: VarRef, reaction: Reaction) = - when (val variable = varRef.variable) { - is Action -> "LF_ACTION_REGISTER_OBSERVER(self->${varRef.name}, ${reaction.codeName});" - is Port -> registerPortObserver(varRef, variable, reaction) - else -> throw AssertionError("Unexpected variable type") + } 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.codeWidth - 1)).toList().joinToString(separator = "\n") { + "LF_PORT_REGISTER_EFFECT(self->${varRef.container.name}[${it}].${port.name}, ${reaction.nameInReactor}, ${port.width})" } - - private fun generateReactionCtor(reaction: Reaction) = - "LF_DEFINE_REACTION_CTOR(${reactor.codeType}, ${reaction.codeName}, ${reaction.index} ${reaction.ctorDeadlineArg});" - - 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});" + } 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.codeWidth - 1)).toList().joinToString(separator = "\n") { + "LF_PORT_REGISTER_OBSERVER(self->${varRef.container.name}[${it}].${port.name}, ${reaction.nameInReactor}, ${port.width})" } - - fun generateReactionCtors() = - reactor.allReactions.joinToString( - separator = "\n", - prefix = "// Reaction constructors\n", - postfix = "\n" - ) { generateReactionCtor(it) } - - fun generateReactionBodies() = - reactor.allReactions.joinToString( - separator = "\n", - prefix = "// Reaction bodies\n", - postfix = "\n" - ) { generateReactionBody(it) } - - fun generateReactionDeadlineHandlers() = - reactionsWithDeadline.joinToString( - separator = "\n", - prefix = "// Reaction deadline handlers\n", - postfix="\n" - ) { generateReactionDeadlineHandler(it) } - - private fun generateReactionScope(reaction: Reaction) = with(PrependOperator) { + } else { + "LF_PORT_REGISTER_OBSERVER(self->${varRef.name}, ${reaction.nameInReactor}, ${port.width});" + } + + private fun registerObserver(varRef: VarRef, reaction: Reaction) = + when (val variable = varRef.variable) { + is Action -> "LF_ACTION_REGISTER_OBSERVER(self->${varRef.name}, ${reaction.codeName});" + is Port -> registerPortObserver(varRef, variable, reaction) + else -> throw AssertionError("Unexpected variable type") + } + + private fun generateReactionCtor(reaction: Reaction) = + "LF_DEFINE_REACTION_CTOR(${reactor.codeType}, ${reaction.codeName}, ${reaction.index} ${reaction.ctorDeadlineArg});" + + 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 generateReactionBodies() = + reactor.allReactions.joinToString( + separator = "\n", prefix = "// Reaction bodies\n", postfix = "\n") { + generateReactionBody(it) + } + + fun generateReactionDeadlineHandlers() = + reactionsWithDeadline.joinToString( + separator = "\n", prefix = "// Reaction deadline handlers\n", postfix = "\n") { + generateReactionDeadlineHandler(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() - } + """ + .trimMargin() + } - private fun generateReactionDeadlineHandler(reaction: Reaction) = with(PrependOperator) { + private fun generateReactionDeadlineHandler(reaction: Reaction) = + with(PrependOperator) { """ |LF_DEFINE_REACTION_DEADLINE_HANDLER(${reactor.codeType}, ${reaction.codeName}) { ${"| "..generateReactionScope(reaction)} | // Start of user-witten reaction body ${"| "..reaction.deadline.code.toText()} |} - """.trimMargin() - } + """ + .trimMargin() + } - private fun generateReactionBody(reaction: Reaction) = with(PrependOperator) { + private fun generateReactionBody(reaction: Reaction) = + with(PrependOperator) { """ |LF_DEFINE_REACTION_BODY(${reactor.codeType}, ${reaction.codeName}) { ${"| "..generateReactionScope(reaction)} | // Start of user-witten reaction deadline handler ${"| "..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) = with(PrependOperator) { + """ + .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 + ) = + with(PrependOperator) { """| |${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) { + """ + .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} { @@ -277,10 +303,12 @@ class UcReactionGenerator(private val reactor: Reactor) { |}; |struct _${inst.reactor.codeType}_${inst.name} ${inst.name}; ${"|"..triggers.joinToString(separator = "\n") {generateContainedTriggerFieldInit("${inst.name}", it)}} - """.trimMargin() - } + """ + .trimMargin() + } - private fun generateContainedBankScope(triggers: List, inst: Instantiation) = with(PrependOperator) { + 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} { @@ -291,84 +319,99 @@ class UcReactionGenerator(private val reactor: Reactor) { |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") { with(PrependOperator) { 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) { + """ + .trimMargin() + } + + private fun generateTriggersEffectsAndSourcesInScope(reaction: Reaction) = + reaction.allUncontainedTriggers + .plus(reaction.allUncontainedEffects) + .plus(reaction.allUncontainedSources) + .joinToString(separator = "\n") { with(PrependOperator) { 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}); ${" | "..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.filter { it is VarRef && it.variable == port }.isNotEmpty()) { - res.add(reaction) - } - } - return res + """ + .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 + .filter { it is VarRef && it.variable == port } + .isNotEmpty()) { + res.add(reaction) + } } - - fun getParentReactionObserversOfOutput(inst: Instantiation, port: Output): List { - val res = mutableListOf(); - for (reaction in reactor.allReactions) { - if (reaction.allContainedSources.filter { it is VarRef && it.variable == port }.isNotEmpty()) { - res.add(reaction) - } - } - return res + return res + } + + fun getParentReactionObserversOfOutput(inst: Instantiation, port: Output): List { + val res = mutableListOf() + for (reaction in reactor.allReactions) { + if (reaction.allContainedSources + .filter { it is VarRef && it.variable == port } + .isNotEmpty()) { + res.add(reaction) + } } - - fun getParentReactionSourcesOfInput(inst: Instantiation, port: Input): List { - val res = mutableListOf(); - for (reaction in reactor.allReactions) { - if (reaction.allContainedEffects.filter { it is VarRef && it.variable == port }.isNotEmpty()) { - res.add(reaction) - } - } - return res + return res + } + + fun getParentReactionSourcesOfInput(inst: Instantiation, port: Input): List { + val res = mutableListOf() + for (reaction in reactor.allReactions) { + if (reaction.allContainedEffects + .filter { it is VarRef && it.variable == port } + .isNotEmpty()) { + 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/uc/UcReactorGenerator.kt index 4b47c9e8..9e843f82 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt @@ -8,87 +8,124 @@ import org.lflang.generator.uc.UcInstanceGenerator.Companion.width import org.lflang.generator.uc.UcPortGenerator.Companion.width import org.lflang.lf.* -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; +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 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 fun takesExtraParameters(): Boolean = + parameters.generateReactorCtorDefArguments().isNotEmpty() || + ports.generateReactorCtorDefArguments().isNotEmpty() + + private fun generateReactorCtorSignature(): String = + if (takesExtraParameters()) + "LF_REACTOR_CTOR_SIGNATURE_WITH_PARAMETERS(${reactor.codeType} ${ports.generateReactorCtorDefArguments()} ${parameters.generateReactorCtorDefArguments()} )" + else "LF_REACTOR_CTOR_SIGNATURE(${reactor.codeType})" + + fun generateReactorPrivatePreamble() = + reactor.allPreambles.joinToString( + prefix = "// Private preambles\n", separator = "\n", postfix = "\n") { + it.code.toText() + } + + 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 getMaxNumPendingEvents(): Int { + var numEvents = reactor.allTimers.count() + for (action in reactor.allActions) { + numEvents += action.maxNumPendingEvents } + numEvents += connections.getMaxNumPendingEvents() + return numEvents + } - private val numChildren = reactor.allInstantiations.map { it.codeWidth }.sum() - - private val parameters = UcParameterGenerator(reactor) - private val connections = UcConnectionGenerator(reactor, null, emptyList()) - 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 fun takesExtraParameters(): Boolean = - parameters.generateReactorCtorDefArguments().isNotEmpty() || ports.generateReactorCtorDefArguments() - .isNotEmpty() - - private fun generateReactorCtorSignature(): String = - if (takesExtraParameters()) "LF_REACTOR_CTOR_SIGNATURE_WITH_PARAMETERS(${reactor.codeType} ${ports.generateReactorCtorDefArguments()} ${parameters.generateReactorCtorDefArguments()} )" - else "LF_REACTOR_CTOR_SIGNATURE(${reactor.codeType})" - - fun generateReactorPrivatePreamble() = reactor.allPreambles.joinToString(prefix= "// Private preambles\n", separator = "\n", postfix = "\n") { it.code.toText()} - - 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 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) { + private fun generateReactorStruct() = + with(PrependOperator) { """ |typedef struct { | Reactor super; @@ -103,10 +140,12 @@ class UcReactorGenerator(private val reactor: Reactor, private val fileConfig: U | LF_REACTOR_BOOKKEEPING_INSTANCES(${reactor.reactions.size}, ${numTriggers()}, ${numChildren}); |} ${reactor.codeType}; | - """.trimMargin() - } + """ + .trimMargin() + } - private fun generateCtorDefinition() = with(PrependOperator) { + private fun generateCtorDefinition() = + with(PrependOperator) { """ |${generateReactorCtorSignature()} { | LF_REACTOR_CTOR_PREAMBLE(); @@ -121,10 +160,12 @@ class UcReactorGenerator(private val reactor: Reactor, private val fileConfig: U ${" | "..reactions.generateReactorCtorCodes()} |} | - """.trimMargin() - } + """ + .trimMargin() + } - fun generateHeader() = with(PrependOperator) { + fun generateHeader() = + with(PrependOperator) { """ |#ifndef ${reactor.includeGuard} |#define ${reactor.includeGuard} @@ -144,10 +185,12 @@ class UcReactorGenerator(private val reactor: Reactor, private val fileConfig: U |${generateReactorCtorSignature()}; | |#endif // ${reactor.includeGuard} - """.trimMargin() - } + """ + .trimMargin() + } - fun generateSource() = with(PrependOperator) { + fun generateSource() = + with(PrependOperator) { """ |#include "${headerFile}" ${" |"..generateReactorPrivatePreamble()} @@ -160,7 +203,7 @@ class UcReactorGenerator(private val reactor: Reactor, private val fileConfig: U ${" |"..connections.generateCtors()} ${" |"..generateCtorDefinition()} | - """.trimMargin() - } - + """ + .trimMargin() + } } 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 index 95ee58e4..f2763039 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStateGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStateGenerator.kt @@ -5,27 +5,29 @@ 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.lf.StateVar 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 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") { + 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()}; + """|${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() + """ + .trimMargin() } else { - "self->${it.name} = ${it.init.expr.toCCode()};" + "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 index 7ef52cfb..480e2533 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcTimerGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcTimerGenerator.kt @@ -9,25 +9,34 @@ 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)} + 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 index d298aa5f..8b7d3324 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcTypes.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcTypes.kt @@ -23,34 +23,35 @@ */ 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 supportsGenerics() = true + + override fun getTargetTimeType() = "interval_t" + + override fun getTargetTagType() = "tag_t" - override fun getTargetTimeType() = "interval_t" - override fun getTargetTagType() = "tag_t" - override fun getTargetUndefinedType() = "void" + override fun getTargetUndefinedType() = "void" - override fun getTargetTimeExpr(timeValue: TimeValue): String = - with(timeValue) { - if (magnitude == 0L) "0" - else "${unit.cUnit}(${magnitude.toString()})" - } + 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" + 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 -> "" - } + TimeUnit.HOUR -> "HOUR" + TimeUnit.DAY -> "DAY" + TimeUnit.WEEK -> "WEEK" + else -> "" + } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcUARTDevices.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcUARTDevices.kt new file mode 100644 index 00000000..b91a5d64 --- /dev/null +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcUARTDevices.kt @@ -0,0 +1,45 @@ +package org.lflang.generator.uc + +import java.util.concurrent.atomic.AtomicInteger + +enum class UARTParityBits { + UART_PARITY_NONE, + UART_PARITY_EVEN, + UART_PARITY_ODD, + UART_PARITY_MARK, + UART_PARITY_SPACE +} + +enum class UARTDataBits { + UART_DATA_BITS_5, + UART_DATA_BITS_6, + UART_DATA_BITS_7, + UART_DATA_BITS_8 +} + +enum class UARTStopBits { + UART_STOP_BITS_1, + UART_STOP_BITS_2 +} + +object UARTDeviceManager { + private val currentPort = AtomicInteger(0) // Starting port number + private val usedPorts = mutableSetOf() + + @Synchronized + fun acquireUARTDevice(): Int { + while (true) { + val port = currentPort.getAndIncrement() + if (port in 0..255 && usedPorts.add(port)) { + return port + } + } + } + + @Synchronized + fun reserve(port: Int) { + assert(port < 65535) + assert(!usedPorts.contains(port)) + usedPorts.add(port) + } +} 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 index 766cbbba..60614961 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcValidator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcValidator.kt @@ -1,45 +1,48 @@ 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 -import java.nio.file.Path 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); +) : 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 getPriority(): Int = 0 + override fun getErrorReportingStrategy() = DiagnosticReporting.Strategy { _, _, _ -> } - override fun isFullBatch(): Boolean = true -} - override fun getPossibleStrategies(): Collection = listOf(ucValidationStrategy) + 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 - ) + override fun getBuildReportingStrategies(): + Pair = + Pair( + ucValidationStrategy.errorReportingStrategy, ucValidationStrategy.outputReportingStrategy) }