Search in sources :

Example 11 with Action

use of org.lflang.lf.Action in project lingua-franca by lf-lang.

the class FederateInstance method findNearestPhysicalActionTrigger.

/**
 * Find the nearest (shortest) path to a physical action trigger from this
 * 'reaction' in terms of minimum delay.
 *
 * @param reaction The reaction to start with
 * @return The minimum delay found to the nearest physical action and
 *  TimeValue.MAX_VALUE otherwise
 */
public TimeValue findNearestPhysicalActionTrigger(ReactionInstance reaction) {
    TimeValue minDelay = TimeValue.MAX_VALUE;
    for (TriggerInstance<? extends Variable> trigger : reaction.triggers) {
        if (trigger.getDefinition() instanceof Action) {
            Action action = (Action) trigger.getDefinition();
            ActionInstance actionInstance = (ActionInstance) trigger;
            if (action.getOrigin() == ActionOrigin.PHYSICAL) {
                if (actionInstance.getMinDelay().isEarlierThan(minDelay)) {
                    minDelay = actionInstance.getMinDelay();
                }
            } else if (action.getOrigin() == ActionOrigin.LOGICAL) {
                // Follow it upstream inside the reactor
                for (ReactionInstance uReaction : actionInstance.getDependsOnReactions()) {
                    // Avoid a loop
                    if (!Objects.equal(uReaction, reaction)) {
                        TimeValue uMinDelay = actionInstance.getMinDelay().add(findNearestPhysicalActionTrigger(uReaction));
                        if (uMinDelay.isEarlierThan(minDelay)) {
                            minDelay = uMinDelay;
                        }
                    }
                }
            }
        } else if (trigger.getDefinition() instanceof Output) {
            // Outputs of contained reactions
            PortInstance outputInstance = (PortInstance) trigger;
            for (ReactionInstance uReaction : outputInstance.getDependsOnReactions()) {
                TimeValue uMinDelay = findNearestPhysicalActionTrigger(uReaction);
                if (uMinDelay.isEarlierThan(minDelay)) {
                    minDelay = uMinDelay;
                }
            }
        }
    }
    return minDelay;
}
Also used : Action(org.lflang.lf.Action) PortInstance(org.lflang.generator.PortInstance) ActionInstance(org.lflang.generator.ActionInstance) ReactionInstance(org.lflang.generator.ReactionInstance) Output(org.lflang.lf.Output) TimeValue(org.lflang.TimeValue)

Example 12 with Action

use of org.lflang.lf.Action in project lingua-franca by lf-lang.

the class CGeneratorExtension method initializeTriggerForControlReactions.

/**
 * Generate C code that initializes three critical structures that support
 * network control reactions:
 *  - triggers_for_network_input_control_reactions: These are triggers that are
 *  used at runtime to insert network input control reactions into the
 *  reaction queue. There could be multiple network input control reactions
 *  for one network input at multiple levels in the hierarchy.
 *  - trigger_for_network_output_control_reactions: Triggers for
 *  network output control reactions, which are unique per each output port.
 *  There could be multiple network output control reactions for each network
 *  output port if it is connected to multiple downstream federates.
 *
 * @param instance  The reactor instance that is at any level of the
 *                  hierarchy within the federate.
 * @param federate  The top-level federate
 * @param generator The instance of the CGenerator passed to keep this
 *                  extension function static.
 * @return A string that initializes the aforementioned three structures.
 */
public static StringBuilder initializeTriggerForControlReactions(ReactorInstance instance, FederateInstance federate, CGenerator generator) {
    StringBuilder builder = new StringBuilder();
    // reactor
    if (instance != generator.main) {
        return builder;
    }
    ReactorDecl reactorClass = instance.getDefinition().getReactorClass();
    Reactor reactor = ASTUtils.toDefinition(reactorClass);
    String nameOfSelfStruct = CUtil.reactorRef(instance);
    // Initialize triggers for network input control reactions
    for (Action trigger : federate.networkInputControlReactionsTriggers) {
        // Check if the trigger belongs to this reactor instance
        if (ASTUtils.allReactions(reactor).stream().anyMatch(r -> {
            return r.getTriggers().stream().anyMatch(t -> {
                if (t instanceof VarRef) {
                    return ((VarRef) t).getVariable().equals(trigger);
                } else {
                    return false;
                }
            });
        })) {
            // Initialize the triggers_for_network_input_control_reactions for the input
            builder.append("// Add trigger " + nameOfSelfStruct + "->_lf__" + trigger.getName() + " to the global list of network input ports.\n" + "_fed.triggers_for_network_input_control_reactions[" + federate.networkInputControlReactionsTriggers.indexOf(trigger) + "]= &" + nameOfSelfStruct + "" + "->_lf__" + trigger.getName() + ";\n");
        }
    }
    nameOfSelfStruct = CUtil.reactorRef(instance);
    // Initialize the trigger for network output control reactions if it doesn't exist.
    if (federate.networkOutputControlReactionsTrigger != null) {
        builder.append("_fed.trigger_for_network_output_control_reactions=&" + nameOfSelfStruct + "->_lf__outputControlReactionTrigger;\n");
    }
    return builder;
}
Also used : VarRef(org.lflang.lf.VarRef) Action(org.lflang.lf.Action) ReactorDecl(org.lflang.lf.ReactorDecl) Reactor(org.lflang.lf.Reactor)

Example 13 with Action

use of org.lflang.lf.Action in project lingua-franca by lf-lang.

the class CReactionGenerator method generateIntendedTagInheritence.

/**
 * Generate code that passes existing intended tag to all output ports
 * and actions. This intended tag is the minimum intended tag of the
 * triggering inputs of the reaction.
 *
 * @param body The body of the reaction. Used to check for the DISABLE_REACTION_INITIALIZATION_MARKER.
 * @param reaction The initialization code will be generated for this specific reaction
 * @param decl The reactor that has the reaction
 * @param reactionIndex The index of the reaction relative to other reactions in the reactor, starting from 0
 */
public static String generateIntendedTagInheritence(String body, Reaction reaction, ReactorDecl decl, int reactionIndex, CTypes types, boolean isFederatedAndDecentralized) {
    // Construct the intended_tag inheritance code to go into
    // the body of the function.
    CodeBuilder intendedTagInheritenceCode = new CodeBuilder();
    // Check if the coordination mode is decentralized and if the reaction has any effects to inherit the STP violation
    if (isFederatedAndDecentralized && !(reaction.getEffects() == null || reaction.getEffects().isEmpty())) {
        intendedTagInheritenceCode.pr(String.join("\n", "#pragma GCC diagnostic push", "#pragma GCC diagnostic ignored \"-Wunused-variable\"", "if (self->_lf__reaction_" + reactionIndex + ".is_STP_violated == true) {"));
        intendedTagInheritenceCode.indent();
        intendedTagInheritenceCode.pr(String.join("\n", "// The operations inside this if clause (if any exists) are expensive ", "// and must only be done if the reaction has unhandled STP violation.", "// Otherwise, all intended_tag values are (NEVER, 0) by default.", "", "// Inherited intended tag. This will take the minimum", "// intended_tag of all input triggers", types.getTargetTagType() + " inherited_min_intended_tag = (" + types.getTargetTagType() + ") { .time = FOREVER, .microstep = UINT_MAX };"));
        intendedTagInheritenceCode.pr("// Find the minimum intended tag");
        // value of intended_tag to choose the minimum.
        for (TriggerRef inputTrigger : ASTUtils.convertToEmptyListIfNull(reaction.getTriggers())) {
            if (inputTrigger instanceof VarRef) {
                VarRef inputTriggerAsVarRef = (VarRef) inputTrigger;
                Variable variable = inputTriggerAsVarRef.getVariable();
                String variableName = inputTriggerAsVarRef.getVariable().getName();
                if (variable instanceof Output) {
                    // Output from a contained reactor
                    String containerName = inputTriggerAsVarRef.getContainer().getName();
                    Output outputPort = (Output) variable;
                    if (ASTUtils.isMultiport(outputPort)) {
                        intendedTagInheritenceCode.pr(String.join("\n", "for (int i=0; i < " + containerName + "." + generateWidthVariable(variableName) + "; i++) {", "    if (compare_tags(" + containerName + "." + variableName + "[i]->intended_tag,", "                        inherited_min_intended_tag) < 0) {", "        inherited_min_intended_tag = " + containerName + "." + variableName + "[i]->intended_tag;", "    }", "}"));
                    } else
                        intendedTagInheritenceCode.pr(String.join("\n", "if (compare_tags(" + containerName + "." + variableName + "->intended_tag,", "                    inherited_min_intended_tag) < 0) {", "    inherited_min_intended_tag = " + containerName + "." + variableName + "->intended_tag;", "}"));
                } else if (variable instanceof Port) {
                    // Input port
                    Port inputPort = (Port) variable;
                    if (ASTUtils.isMultiport(inputPort)) {
                        intendedTagInheritenceCode.pr(String.join("\n", "for (int i=0; i < " + generateWidthVariable(variableName) + "; i++) {", "    if (compare_tags(" + variableName + "[i]->intended_tag, inherited_min_intended_tag) < 0) {", "        inherited_min_intended_tag = " + variableName + "[i]->intended_tag;", "    }", "}"));
                    } else {
                        intendedTagInheritenceCode.pr(String.join("\n", "if (compare_tags(" + variableName + "->intended_tag, inherited_min_intended_tag) < 0) {", "    inherited_min_intended_tag = " + variableName + "->intended_tag;", "}"));
                    }
                } else if (variable instanceof Action) {
                    intendedTagInheritenceCode.pr(String.join("\n", "if (compare_tags(" + variableName + "->trigger->intended_tag, inherited_min_intended_tag) < 0) {", "    inherited_min_intended_tag = " + variableName + "->trigger->intended_tag;", "}"));
                }
            }
        }
        if (reaction.getTriggers() == null || reaction.getTriggers().size() == 0) {
            // NOTE: this does not include contained outputs.
            for (Input input : ((Reactor) reaction.eContainer()).getInputs()) {
                intendedTagInheritenceCode.pr(String.join("\n", "if (compare_tags(" + input.getName() + "->intended_tag, inherited_min_intended_tag) > 0) {", "    inherited_min_intended_tag = " + input.getName() + "->intended_tag;", "}"));
            }
        }
        // Once the minimum intended tag has been found,
        // it will be passed down to the port effects
        // of the reaction. Note that the intended tag
        // will not pass on to actions downstream.
        // Last reaction that sets the intended tag for the effect
        // will be seen.
        intendedTagInheritenceCode.pr(String.join("\n", "// All effects inherit the minimum intended tag of input triggers", "if (inherited_min_intended_tag.time != NEVER) {"));
        intendedTagInheritenceCode.indent();
        for (VarRef effect : ASTUtils.convertToEmptyListIfNull(reaction.getEffects())) {
            Variable effectVar = effect.getVariable();
            Instantiation effContainer = effect.getContainer();
            if (effectVar instanceof Input) {
                if (ASTUtils.isMultiport((Port) effectVar)) {
                    intendedTagInheritenceCode.pr(String.join("\n", "for(int i=0; i < " + effContainer.getName() + "." + generateWidthVariable(effectVar.getName()) + "; i++) {", "    " + effContainer.getName() + "." + effectVar.getName() + "[i]->intended_tag = inherited_min_intended_tag;", "}"));
                } else {
                    if (effContainer.getWidthSpec() != null) {
                        // Contained reactor is a bank.
                        intendedTagInheritenceCode.pr(String.join("\n", "for (int bankIndex = 0; bankIndex < self->_lf_" + generateWidthVariable(effContainer.getName()) + "; bankIndex++) {", "    " + effContainer.getName() + "[bankIndex]." + effectVar.getName() + " = &(self->_lf_" + effContainer.getName() + "[bankIndex]." + effectVar.getName() + ");", "}"));
                    } else {
                        // Input to a contained reaction
                        intendedTagInheritenceCode.pr(String.join("\n", "// Don't reset the intended tag of the output port if it has already been set.", effContainer.getName() + "." + effectVar.getName() + "->intended_tag = inherited_min_intended_tag;"));
                    }
                }
            }
        }
        intendedTagInheritenceCode.unindent();
        intendedTagInheritenceCode.pr("}");
        intendedTagInheritenceCode.unindent();
        intendedTagInheritenceCode.pr("#pragma GCC diagnostic pop");
        intendedTagInheritenceCode.pr("}");
    }
    return intendedTagInheritenceCode.toString();
}
Also used : VarRef(org.lflang.lf.VarRef) Action(org.lflang.lf.Action) Input(org.lflang.lf.Input) Variable(org.lflang.lf.Variable) CUtil.generateWidthVariable(org.lflang.generator.c.CUtil.generateWidthVariable) Output(org.lflang.lf.Output) Port(org.lflang.lf.Port) TriggerRef(org.lflang.lf.TriggerRef) Reactor(org.lflang.lf.Reactor) Instantiation(org.lflang.lf.Instantiation) CodeBuilder(org.lflang.generator.CodeBuilder)

Example 14 with Action

use of org.lflang.lf.Action in project lingua-franca by lf-lang.

the class CReactionGenerator method generateInitializationForReaction.

/**
 * Generate necessary initialization code inside the body of the reaction that belongs to reactor decl.
 * @param body The body of the reaction. Used to check for the DISABLE_REACTION_INITIALIZATION_MARKER.
 * @param reaction The initialization code will be generated for this specific reaction
 * @param decl The reactor that has the reaction
 * @param reactionIndex The index of the reaction relative to other reactions in the reactor, starting from 0
 */
public static String generateInitializationForReaction(String body, Reaction reaction, ReactorDecl decl, int reactionIndex, CTypes types, ErrorReporter errorReporter, Instantiation mainDef, boolean isFederatedAndDecentralized, boolean requiresTypes) {
    Reactor reactor = ASTUtils.toDefinition(decl);
    // Construct the reactionInitialization code to go into
    // the body of the function before the verbatim code.
    CodeBuilder reactionInitialization = new CodeBuilder();
    CodeBuilder code = new CodeBuilder();
    // Define the "self" struct.
    String structType = CUtil.selfType(decl);
    // or anything else. No need to declare it.
    if (structType != null) {
        code.pr(String.join("\n", "#pragma GCC diagnostic push", "#pragma GCC diagnostic ignored \"-Wunused-variable\"", structType + "* self = (" + structType + "*)instance_args;"));
    }
    // to not generate it.
    if (body.startsWith(CGenerator.DISABLE_REACTION_INITIALIZATION_MARKER)) {
        code.pr("#pragma GCC diagnostic pop");
        return code.toString();
    }
    // A reaction may send to or receive from multiple ports of
    // a contained reactor. The variables for these ports need to
    // all be declared as fields of the same struct. Hence, we first
    // collect the fields to be defined in the structs and then
    // generate the structs.
    Map<Instantiation, CodeBuilder> fieldsForStructsForContainedReactors = new LinkedHashMap<>();
    // Actions may appear twice, first as a trigger, then with the outputs.
    // But we need to declare it only once. Collect in this data structure
    // the actions that are declared as triggered so that if they appear
    // again with the outputs, they are not defined a second time.
    // That second redefinition would trigger a compile error.
    Set<Action> actionsAsTriggers = new LinkedHashSet<>();
    // defined so that they can be used in the verbatim code.
    for (TriggerRef trigger : ASTUtils.convertToEmptyListIfNull(reaction.getTriggers())) {
        if (trigger instanceof VarRef) {
            VarRef triggerAsVarRef = (VarRef) trigger;
            if (triggerAsVarRef.getVariable() instanceof Port) {
                generatePortVariablesInReaction(reactionInitialization, fieldsForStructsForContainedReactors, triggerAsVarRef, decl, types);
            } else if (triggerAsVarRef.getVariable() instanceof Action) {
                reactionInitialization.pr(generateActionVariablesInReaction((Action) triggerAsVarRef.getVariable(), decl, types));
                actionsAsTriggers.add((Action) triggerAsVarRef.getVariable());
            }
        }
    }
    if (reaction.getTriggers() == null || reaction.getTriggers().size() == 0) {
        // NOTE: this does not include contained outputs.
        for (Input input : reactor.getInputs()) {
            reactionInitialization.pr(generateInputVariablesInReaction(input, decl, types));
        }
    }
    // Define argument for non-triggering inputs.
    for (VarRef src : ASTUtils.convertToEmptyListIfNull(reaction.getSources())) {
        if (src.getVariable() instanceof Port) {
            generatePortVariablesInReaction(reactionInitialization, fieldsForStructsForContainedReactors, src, decl, types);
        } else if (src.getVariable() instanceof Action) {
            // It's a bit odd to read but not be triggered by an action, but
            // OK, I guess we allow it.
            reactionInitialization.pr(generateActionVariablesInReaction((Action) src.getVariable(), decl, types));
            actionsAsTriggers.add((Action) src.getVariable());
        }
    }
    // value that may have been written to that output in an earlier reaction.
    if (reaction.getEffects() != null) {
        for (VarRef effect : reaction.getEffects()) {
            Variable variable = effect.getVariable();
            if (variable instanceof Action) {
                // If it has already appeared as trigger, do not redefine it.
                if (!actionsAsTriggers.contains(effect.getVariable())) {
                    reactionInitialization.pr(CGenerator.variableStructType(variable, decl) + "* " + variable.getName() + " = &self->_lf_" + variable.getName() + ";");
                }
            } else if (effect.getVariable() instanceof Mode) {
                // Mode change effect
                int idx = ASTUtils.allModes(reactor).indexOf((Mode) effect.getVariable());
                String name = effect.getVariable().getName();
                if (idx >= 0) {
                    reactionInitialization.pr("reactor_mode_t* " + name + " = &self->_lf__modes[" + idx + "];\n" + "char _lf_" + name + "_change_type = " + (ModeTransitionType.getModeTransitionType(effect) == ModeTransitionType.HISTORY ? 2 : 1) + ";");
                } else {
                    errorReporter.reportError(reaction, "In generateReaction(): " + name + " not a valid mode of this reactor.");
                }
            } else {
                if (variable instanceof Output) {
                    reactionInitialization.pr(generateOutputVariablesInReaction(effect, decl, errorReporter, requiresTypes));
                } else if (variable instanceof Input) {
                    // It is the input of a contained reactor.
                    generateVariablesForSendingToContainedReactors(reactionInitialization, fieldsForStructsForContainedReactors, effect.getContainer(), (Input) variable);
                } else {
                    errorReporter.reportError(reaction, "In generateReaction(): effect is neither an input nor an output.");
                }
            }
        }
    }
    // generate the structs used for communication to and from contained reactors.
    for (Instantiation containedReactor : fieldsForStructsForContainedReactors.keySet()) {
        String array = "";
        if (containedReactor.getWidthSpec() != null) {
            String containedReactorWidthVar = generateWidthVariable(containedReactor.getName());
            code.pr("int " + containedReactorWidthVar + " = self->_lf_" + containedReactorWidthVar + ";");
            // Windows does not support variables in arrays declared on the stack,
            // so we use the maximum size over all bank members.
            array = "[" + maxContainedReactorBankWidth(containedReactor, null, 0, mainDef) + "]";
        }
        code.pr(String.join("\n", "struct " + containedReactor.getName() + " {", "    " + fieldsForStructsForContainedReactors.get(containedReactor) + "", "} " + containedReactor.getName() + array + ";"));
    }
    // Next generate all the collected setup code.
    code.pr(reactionInitialization.toString());
    code.pr("#pragma GCC diagnostic pop");
    if (reaction.getStp() == null) {
        // Pass down the intended_tag to all input and output effects
        // downstream if the current reaction does not have a STP
        // handler.
        code.pr(generateIntendedTagInheritence(body, reaction, decl, reactionIndex, types, isFederatedAndDecentralized));
    }
    return code.toString();
}
Also used : LinkedHashSet(java.util.LinkedHashSet) VarRef(org.lflang.lf.VarRef) Action(org.lflang.lf.Action) Variable(org.lflang.lf.Variable) CUtil.generateWidthVariable(org.lflang.generator.c.CUtil.generateWidthVariable) Port(org.lflang.lf.Port) Mode(org.lflang.lf.Mode) TriggerRef(org.lflang.lf.TriggerRef) CodeBuilder(org.lflang.generator.CodeBuilder) LinkedHashMap(java.util.LinkedHashMap) Input(org.lflang.lf.Input) Output(org.lflang.lf.Output) Reactor(org.lflang.lf.Reactor) Instantiation(org.lflang.lf.Instantiation)

Aggregations

Action (org.lflang.lf.Action)14 Reactor (org.lflang.lf.Reactor)10 VarRef (org.lflang.lf.VarRef)8 Output (org.lflang.lf.Output)6 Input (org.lflang.lf.Input)5 TriggerRef (org.lflang.lf.TriggerRef)4 LinkedHashSet (java.util.LinkedHashSet)3 TimeValue (org.lflang.TimeValue)3 CodeBuilder (org.lflang.generator.CodeBuilder)3 Instantiation (org.lflang.lf.Instantiation)3 LfFactory (org.lflang.lf.LfFactory)3 Port (org.lflang.lf.Port)3 Reaction (org.lflang.lf.Reaction)3 LinkedHashMap (java.util.LinkedHashMap)2 InferredType (org.lflang.InferredType)2 CoordinationType (org.lflang.TargetProperty.CoordinationType)2 PortInstance (org.lflang.generator.PortInstance)2 CUtil.generateWidthVariable (org.lflang.generator.c.CUtil.generateWidthVariable)2 ImportedReactor (org.lflang.lf.ImportedReactor)2 Parameter (org.lflang.lf.Parameter)2