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;
}
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;
}
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();
}
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();
}
Aggregations