use of org.lflang.lf.VarRef in project lingua-franca by lf-lang.
the class FederateInstance method containsAllVarRefs.
/**
* Return true if all members of 'varRefs' belong to this federate.
*
* As a convenience measure, if some members of 'varRefs' are from
* different federates, also report an error.
*
* @param varRefs A collection of VarRefs
*/
private boolean containsAllVarRefs(Iterable<VarRef> varRefs) {
var referencesFederate = false;
var inFederate = true;
for (VarRef varRef : varRefs) {
if (varRef.getContainer() == this.instantiation) {
referencesFederate = true;
} else {
if (referencesFederate) {
errorReporter.reportError(varRef, "Mixed triggers and effects from" + " different federates. This is not permitted");
}
inFederate = false;
}
}
return inFederate;
}
use of org.lflang.lf.VarRef 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.VarRef in project lingua-franca by lf-lang.
the class LFValidator method checkConnection.
@Check(CheckType.FAST)
public void checkConnection(Connection connection) {
// Report if connection is part of a cycle.
Set<NamedInstance<?>> cycles = this.info.topologyCycles();
for (VarRef lp : connection.getLeftPorts()) {
for (VarRef rp : connection.getRightPorts()) {
boolean leftInCycle = false;
for (NamedInstance<?> it : cycles) {
if ((lp.getContainer() == null && it.getDefinition().equals(lp.getVariable())) || (it.getDefinition().equals(lp.getVariable()) && it.getParent().equals(lp.getContainer()))) {
leftInCycle = true;
break;
}
}
for (NamedInstance<?> it : cycles) {
if ((rp.getContainer() == null && it.getDefinition().equals(rp.getVariable())) || (it.getDefinition().equals(rp.getVariable()) && it.getParent().equals(rp.getContainer()))) {
if (leftInCycle) {
Reactor reactor = ASTUtils.getEnclosingReactor(connection);
String reactorName = reactor.getName();
error(String.format("Connection in reactor %s creates", reactorName) + String.format("a cyclic dependency between %s and %s.", toText(lp), toText(rp)), Literals.CONNECTION__DELAY);
}
}
}
}
}
// we leave type compatibility that language's compiler or interpreter.
if (isCBasedTarget()) {
Type type = (Type) null;
for (VarRef port : connection.getLeftPorts()) {
// error. Avoid a class cast exception.
if (port.getVariable() instanceof Port) {
if (type == null) {
type = ((Port) port.getVariable()).getType();
} else {
// method for AST types, so we have to manually check the types.
if (!sameType(type, ((Port) port.getVariable()).getType())) {
error("Types do not match.", Literals.CONNECTION__LEFT_PORTS);
}
}
}
}
for (VarRef port : connection.getRightPorts()) {
// error. Avoid a class cast exception.
if (port.getVariable() instanceof Port) {
if (type == null) {
type = ((Port) port.getVariable()).getType();
} else {
if (!sameType(type, type = ((Port) port.getVariable()).getType())) {
error("Types do not match.", Literals.CONNECTION__RIGHT_PORTS);
}
}
}
}
}
// Check whether the total width of the left side of the connection
// matches the total width of the right side. This cannot be determined
// here if the width is not given as a constant. In that case, it is up
// to the code generator to check it.
int leftWidth = 0;
for (VarRef port : connection.getLeftPorts()) {
// null args imply incomplete check.
int width = inferPortWidth(port, null, null);
if (width < 0 || leftWidth < 0) {
// Cannot determine the width of the left ports.
leftWidth = -1;
} else {
leftWidth += width;
}
}
int rightWidth = 0;
for (VarRef port : connection.getRightPorts()) {
// null args imply incomplete check.
int width = inferPortWidth(port, null, null);
if (width < 0 || rightWidth < 0) {
// Cannot determine the width of the left ports.
rightWidth = -1;
} else {
rightWidth += width;
}
}
if (leftWidth != -1 && rightWidth != -1 && leftWidth != rightWidth) {
if (connection.isIterated()) {
if (leftWidth == 0 || rightWidth % leftWidth != 0) {
// FIXME: The second argument should be Literals.CONNECTION, but
// stupidly, xtext will not accept that. There seems to be no way to
// report an error for the whole connection statement.
warning(String.format("Left width %s does not divide right width %s", leftWidth, rightWidth), Literals.CONNECTION__LEFT_PORTS);
}
} else {
// FIXME: The second argument should be Literals.CONNECTION, but
// stupidly, xtext will not accept that. There seems to be no way to
// report an error for the whole connection statement.
warning(String.format("Left width %s does not match right width %s", leftWidth, rightWidth), Literals.CONNECTION__LEFT_PORTS);
}
}
Reactor reactor = ASTUtils.getEnclosingReactor(connection);
// Make sure the right port is not already an effect of a reaction.
for (Reaction reaction : ASTUtils.allReactions(reactor)) {
for (VarRef effect : reaction.getEffects()) {
for (VarRef rightPort : connection.getRightPorts()) {
if (// Refers to the same variable
rightPort.getVariable().equals(effect.getVariable()) && // Refers to the same instance
rightPort.getContainer() == effect.getContainer() && (// Either is not part of a mode
reaction.eContainer() instanceof Reactor || connection.eContainer() instanceof Reactor || // Or they are in the same mode
connection.eContainer() == reaction.eContainer())) {
error("Cannot connect: Port named '" + effect.getVariable().getName() + "' is already effect of a reaction.", Literals.CONNECTION__RIGHT_PORTS);
}
}
}
}
// upstream connection.
for (Connection c : reactor.getConnections()) {
if (c != connection) {
for (VarRef thisRightPort : connection.getRightPorts()) {
for (VarRef thatRightPort : c.getRightPorts()) {
if (// Refers to the same variable
thisRightPort.getVariable().equals(thatRightPort.getVariable()) && // Refers to the same instance
thisRightPort.getContainer() == thatRightPort.getContainer() && (// Or either of the connections in not part of a mode
connection.eContainer() instanceof Reactor || c.eContainer() instanceof Reactor || // Or they are in the same mode
connection.eContainer() == c.eContainer())) {
error("Cannot connect: Port named '" + thisRightPort.getVariable().getName() + "' may only appear once on the right side of a connection.", Literals.CONNECTION__RIGHT_PORTS);
}
}
}
}
}
}
use of org.lflang.lf.VarRef in project lingua-franca by lf-lang.
the class LFValidator method checkReaction.
@Check(CheckType.FAST)
public void checkReaction(Reaction reaction) {
if (reaction.getTriggers() == null || reaction.getTriggers().size() == 0) {
warning("Reaction has no trigger.", Literals.REACTION__TRIGGERS);
}
HashSet<Variable> triggers = new HashSet<>();
// Make sure input triggers have no container and output sources do.
for (TriggerRef trigger : reaction.getTriggers()) {
if (trigger instanceof VarRef) {
VarRef triggerVarRef = (VarRef) trigger;
triggers.add(triggerVarRef.getVariable());
if (triggerVarRef instanceof Input) {
if (triggerVarRef.getContainer() != null) {
error(String.format("Cannot have an input of a contained reactor as a trigger: %s.%s", triggerVarRef.getContainer().getName(), triggerVarRef.getVariable().getName()), Literals.REACTION__TRIGGERS);
}
} else if (triggerVarRef.getVariable() instanceof Output) {
if (triggerVarRef.getContainer() == null) {
error(String.format("Cannot have an output of this reactor as a trigger: %s", triggerVarRef.getVariable().getName()), Literals.REACTION__TRIGGERS);
}
}
}
}
// Also check that a source is not already listed as a trigger.
for (VarRef source : reaction.getSources()) {
if (triggers.contains(source.getVariable())) {
error(String.format("Source is already listed as a trigger: %s", source.getVariable().getName()), Literals.REACTION__SOURCES);
}
if (source.getVariable() instanceof Input) {
if (source.getContainer() != null) {
error(String.format("Cannot have an input of a contained reactor as a source: %s.%s", source.getContainer().getName(), source.getVariable().getName()), Literals.REACTION__SOURCES);
}
} else if (source.getVariable() instanceof Output) {
if (source.getContainer() == null) {
error(String.format("Cannot have an output of this reactor as a source: %s", source.getVariable().getName()), Literals.REACTION__SOURCES);
}
}
}
// Make sure output effects have no container and input effects do.
for (VarRef effect : reaction.getEffects()) {
if (effect.getVariable() instanceof Input) {
if (effect.getContainer() == null) {
error(String.format("Cannot have an input of this reactor as an effect: %s", effect.getVariable().getName()), Literals.REACTION__EFFECTS);
}
} else if (effect.getVariable() instanceof Output) {
if (effect.getContainer() != null) {
error(String.format("Cannot have an output of a contained reactor as an effect: %s.%s", effect.getContainer().getName(), effect.getVariable().getName()), Literals.REACTION__EFFECTS);
}
}
}
// // Report error if this reaction is part of a cycle.
Set<NamedInstance<?>> cycles = this.info.topologyCycles();
Reactor reactor = ASTUtils.getEnclosingReactor(reaction);
boolean reactionInCycle = false;
for (NamedInstance<?> it : cycles) {
if (it.getDefinition().equals(reaction)) {
reactionInCycle = true;
}
}
if (reactionInCycle) {
// Report involved triggers.
List<CharSequence> trigs = new ArrayList<>();
for (TriggerRef t : reaction.getTriggers()) {
if (!(t instanceof VarRef)) {
continue;
}
VarRef tVarRef = (VarRef) t;
boolean triggerExistsInCycle = false;
for (NamedInstance<?> c : cycles) {
if (c.getDefinition().equals(tVarRef.getVariable())) {
triggerExistsInCycle = true;
break;
}
}
if (triggerExistsInCycle) {
trigs.add(toText(tVarRef));
}
}
if (trigs.size() > 0) {
error(String.format("Reaction triggers involved in cyclic dependency in reactor %s: %s.", reactor.getName(), String.join(", ", trigs)), Literals.REACTION__TRIGGERS);
}
// Report involved sources.
List<CharSequence> sources = new ArrayList<>();
for (VarRef t : reaction.getSources()) {
boolean sourceExistInCycle = false;
for (NamedInstance<?> c : cycles) {
if (c.getDefinition().equals(t.getVariable())) {
sourceExistInCycle = true;
break;
}
}
if (sourceExistInCycle) {
sources.add(toText(t));
}
}
if (sources.size() > 0) {
error(String.format("Reaction sources involved in cyclic dependency in reactor %s: %s.", reactor.getName(), String.join(", ", sources)), Literals.REACTION__SOURCES);
}
// Report involved effects.
List<CharSequence> effects = new ArrayList<>();
for (VarRef t : reaction.getEffects()) {
boolean effectExistInCycle = false;
for (NamedInstance<?> c : cycles) {
if (c.getDefinition().equals(t.getVariable())) {
effectExistInCycle = true;
break;
}
}
if (effectExistInCycle) {
effects.add(toText(t));
}
}
if (effects.size() > 0) {
error(String.format("Reaction effects involved in cyclic dependency in reactor %s: %s.", reactor.getName(), String.join(", ", effects)), Literals.REACTION__EFFECTS);
}
if (trigs.size() + sources.size() == 0) {
error(String.format("Cyclic dependency due to preceding reaction. Consider reordering reactions within reactor %s to avoid causality loop.", reactor.getName()), reaction.eContainer(), Literals.REACTOR__REACTIONS, reactor.getReactions().indexOf(reaction));
} else if (effects.size() == 0) {
error(String.format("Cyclic dependency due to succeeding reaction. Consider reordering reactions within reactor %s to avoid causality loop.", reactor.getName()), reaction.eContainer(), Literals.REACTOR__REACTIONS, reactor.getReactions().indexOf(reaction));
}
// Not reporting reactions that are part of cycle _only_ due to reaction ordering.
// Moving them won't help solve the problem.
}
// FIXME: improve error message.
}
use of org.lflang.lf.VarRef 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();
}
Aggregations