use of org.lflang.lf.Port in project lingua-franca by lf-lang.
the class ASTUtils method inferPortWidth.
/**
* Infer the width of a port reference in a connection.
* The port reference one or two parts, a port and an (optional) container
* which is an Instantiation that may refer to a bank of reactors.
* The width will be the product of the bank width and the port width.
* The returned value will be 1 if the port is not in a bank and is not a multiport.
*
* If the width cannot be determined, this will return -1.
* The width cannot be determined if the list of instantiations is
* missing or incomplete.
*
* The instantiations list is as in
* {@link initialValue(Parameter, List<Instantiation>}.
* The first element on this list should be the instantiation
* that contains the specified connection.
*
* @param reference A port reference.
* @param connection A connection, or null if not in the context of a connection.
* @param instantiations The (optional) list of instantiations.
*
* @return The width or -1 if it could not be determined.
*
* @throws IllegalArgumentException If an instantiation provided is not as
* given above or if the chain of instantiations is not nested.
*/
public static int inferPortWidth(VarRef reference, Connection connection, List<Instantiation> instantiations) {
if (reference.getVariable() instanceof Port) {
// If the port is given as a.b, then we want to prepend a to
// the list of instantiations to determine the width of this port.
List<Instantiation> extended = instantiations;
if (reference.getContainer() != null) {
extended = new ArrayList<>();
extended.add(reference.getContainer());
if (instantiations != null) {
extended.addAll(instantiations);
}
}
int portWidth = width(((Port) reference.getVariable()).getWidthSpec(), extended);
if (portWidth < 0) {
// Could not determine port width.
return -1;
}
// Next determine the bank width. This may be unspecified, in which
// case it has to be inferred using the connection.
int bankWidth = 1;
if (reference.getContainer() != null) {
bankWidth = width(reference.getContainer().getWidthSpec(), instantiations);
if (bankWidth < 0 && connection != null) {
// Try to infer the bank width from the connection.
if (reference.getContainer().getWidthSpec().isOfVariableLength()) {
// This occurs for a bank of delays.
int leftWidth = 0;
int rightWidth = 0;
int leftOrRight = 0;
for (VarRef leftPort : connection.getLeftPorts()) {
if (leftPort == reference) {
if (leftOrRight != 0) {
throw new InvalidSourceException("Multiple ports with variable width on a connection.");
}
// Indicate that this port is on the left.
leftOrRight = -1;
} else {
// The left port is not the same as this reference.
int otherWidth = inferPortWidth(leftPort, connection, instantiations);
if (otherWidth < 0) {
// Cannot determine width.
return -1;
}
leftWidth += otherWidth;
}
}
for (VarRef rightPort : connection.getRightPorts()) {
if (rightPort == reference) {
if (leftOrRight != 0) {
throw new InvalidSourceException("Multiple ports with variable width on a connection.");
}
// Indicate that this port is on the right.
leftOrRight = 1;
} else {
int otherWidth = inferPortWidth(rightPort, connection, instantiations);
if (otherWidth < 0) {
// Cannot determine width.
return -1;
}
rightWidth += otherWidth;
}
}
int discrepancy = 0;
if (leftOrRight < 0) {
// This port is on the left.
discrepancy = rightWidth - leftWidth;
} else if (leftOrRight > 0) {
// This port is on the right.
discrepancy = leftWidth - rightWidth;
}
// Check that portWidth divides the discrepancy.
if (discrepancy % portWidth != 0) {
// This is an error.
return -1;
}
bankWidth = discrepancy / portWidth;
} else {
// Could not determine the bank width.
return -1;
}
}
}
return portWidth * bankWidth;
}
// Argument is not a port.
return -1;
}
use of org.lflang.lf.Port in project lingua-franca by lf-lang.
the class ASTUtils method hasMultipleConnections.
/**
* Return true if the connection involves multiple ports on the left or right side of the connection, or
* if the port on the left or right of the connection involves a bank of reactors or a multiport.
* @param connection The connection.
*/
public static boolean hasMultipleConnections(Connection connection) {
if (connection.getLeftPorts().size() > 1 || connection.getRightPorts().size() > 1) {
return true;
}
VarRef leftPort = connection.getLeftPorts().get(0);
VarRef rightPort = connection.getRightPorts().get(0);
Instantiation leftContainer = leftPort.getContainer();
Instantiation rightContainer = rightPort.getContainer();
Port leftPortAsPort = (Port) leftPort.getVariable();
Port rightPortAsPort = (Port) rightPort.getVariable();
if (leftPortAsPort.getWidthSpec() != null || (leftContainer != null && leftContainer.getWidthSpec() != null) || rightPortAsPort.getWidthSpec() != null || (rightContainer != null && rightContainer.getWidthSpec() != null)) {
return true;
}
return false;
}
use of org.lflang.lf.Port in project lingua-franca by lf-lang.
the class ASTUtils method findConflictingConnectionsInModalReactors.
/**
* Find connections in the given resource that would be conflicting writes if they were not located in mutually
* exclusive modes.
*
* @param resource The AST.
* @return a list of connections being able to be transformed
*/
public static Collection<Connection> findConflictingConnectionsInModalReactors(Resource resource) {
var transform = new HashSet<Connection>();
var reactors = Iterables.filter(IteratorExtensions.toIterable(resource.getAllContents()), Reactor.class);
for (Reactor reactor : reactors) {
if (!reactor.getModes().isEmpty()) {
// Only for modal reactors
var allWriters = HashMultimap.<Pair<Instantiation, Variable>, EObject>create();
// Collect destinations
for (var rea : allReactions(reactor)) {
for (var eff : rea.getEffects()) {
if (eff.getVariable() instanceof Port) {
allWriters.put(Tuples.pair(eff.getContainer(), eff.getVariable()), rea);
}
}
}
for (var con : ASTUtils.<Connection>collectElements(reactor, featurePackage.getReactor_Connections(), false, true)) {
for (var port : con.getRightPorts()) {
allWriters.put(Tuples.pair(port.getContainer(), port.getVariable()), con);
}
}
// Handle conflicting writers
for (var key : allWriters.keySet()) {
var writers = allWriters.get(key);
if (writers.size() > 1) {
// has multiple sources
var writerModes = HashMultimap.<Mode, EObject>create();
// find modes
for (var writer : writers) {
if (writer.eContainer() instanceof Mode) {
writerModes.put((Mode) writer.eContainer(), writer);
} else {
writerModes.put(null, writer);
}
}
// Conflicting connection can only be handled if..
if (// no writer is on root level (outside of modes) and...
!writerModes.containsKey(null) && writerModes.keySet().stream().map(m -> writerModes.get(m)).allMatch(// all writers in a mode are either...
writersInMode -> // the only writer or...
writersInMode.size() == 1 || // all are reactions and hence ordered
writersInMode.stream().allMatch(w -> w instanceof Reaction))) {
// Add connections to transform list
writers.stream().filter(w -> w instanceof Connection).forEach(c -> transform.add((Connection) c));
}
}
}
}
}
return transform;
}
use of org.lflang.lf.Port in project lingua-franca by lf-lang.
the class PythonReactionGenerator method generatePythonReactionParametersAndInitializations.
/**
* Generate parameters and their respective initialization code for a reaction function
* The initialization code is put at the beginning of the reaction before user code
* @param parameters The parameters used for function definition
* @param inits The initialization code for those paramters
* @param decl Reactor declaration
* @param reaction The reaction to be used to generate parameters for
*/
public static void generatePythonReactionParametersAndInitializations(List<String> parameters, CodeBuilder inits, ReactorDecl decl, Reaction reaction) {
Reactor reactor = ASTUtils.toDefinition(decl);
LinkedHashSet<String> generatedParams = new LinkedHashSet<>();
// Handle triggers
for (TriggerRef trigger : ASTUtils.convertToEmptyListIfNull(reaction.getTriggers())) {
if (!(trigger instanceof VarRef)) {
continue;
}
VarRef triggerAsVarRef = (VarRef) trigger;
if (triggerAsVarRef.getVariable() instanceof Port) {
if (triggerAsVarRef.getVariable() instanceof Input) {
if (((Input) triggerAsVarRef.getVariable()).isMutable()) {
generatedParams.add("mutable_" + triggerAsVarRef.getVariable().getName() + "");
// Create a deep copy
if (ASTUtils.isMultiport((Input) triggerAsVarRef.getVariable())) {
inits.pr(triggerAsVarRef.getVariable().getName() + " = [Make() for i in range(len(mutable_" + triggerAsVarRef.getVariable().getName() + "))]");
inits.pr("for i in range(len(mutable_" + triggerAsVarRef.getVariable().getName() + ")):");
inits.pr(" " + triggerAsVarRef.getVariable().getName() + "[i].value = copy.deepcopy(mutable_" + triggerAsVarRef.getVariable().getName() + "[i].value)");
} else {
inits.pr(triggerAsVarRef.getVariable().getName() + " = Make()");
inits.pr(triggerAsVarRef.getVariable().getName() + ".value = copy.deepcopy(mutable_" + triggerAsVarRef.getVariable().getName() + ".value)");
}
} else {
generatedParams.add(triggerAsVarRef.getVariable().getName());
}
} else {
// Handle contained reactors' ports
generatedParams.add(triggerAsVarRef.getContainer().getName() + "_" + triggerAsVarRef.getVariable().getName());
inits.pr(PythonPortGenerator.generatePythonPortVariableInReaction(triggerAsVarRef));
}
} else if (triggerAsVarRef.getVariable() instanceof Action) {
generatedParams.add(triggerAsVarRef.getVariable().getName());
}
}
// Handle non-triggering inputs
if (reaction.getTriggers() == null || reaction.getTriggers().size() == 0) {
for (Input input : ASTUtils.convertToEmptyListIfNull(reactor.getInputs())) {
generatedParams.add(input.getName());
if (input.isMutable()) {
// Create a deep copy
inits.pr(input.getName() + " = copy.deepcopy(" + input.getName() + ")");
}
}
}
for (VarRef src : ASTUtils.convertToEmptyListIfNull(reaction.getSources())) {
if (src.getVariable() instanceof Output) {
// Output of a contained reactor
generatedParams.add(src.getContainer().getName() + "_" + src.getVariable().getName());
inits.pr(PythonPortGenerator.generatePythonPortVariableInReaction(src));
} else {
generatedParams.add(src.getVariable().getName());
if (src.getVariable() instanceof Input) {
if (((Input) src.getVariable()).isMutable()) {
// Create a deep copy
inits.pr(src.getVariable().getName() + " = copy.deepcopy(" + src.getVariable().getName() + ")");
}
}
}
}
// Handle effects
for (VarRef effect : ASTUtils.convertToEmptyListIfNull(reaction.getEffects())) {
if (effect.getVariable() instanceof Input) {
generatedParams.add(effect.getContainer().getName() + "_" + effect.getVariable().getName());
inits.pr(PythonPortGenerator.generatePythonPortVariableInReaction(effect));
} else {
generatedParams.add(effect.getVariable().getName());
if (effect.getVariable() instanceof Port) {
if (ASTUtils.isMultiport((Port) effect.getVariable())) {
// Handle multiports
}
}
}
}
for (String s : generatedParams) {
parameters.add(s);
}
}
use of org.lflang.lf.Port in project lingua-franca by lf-lang.
the class ReactorInstance method listPortInstances.
/**
* Given a list of port references, as found on either side of a connection,
* return a list of the port instance ranges referenced. These may be multiports,
* and may be ports of a contained bank (a port representing ports of the bank
* members) so the returned list includes ranges of banks and channels.
*
* If a given port reference has the form `interleaved(b.m)`, where `b` is
* a bank and `m` is a multiport, then the corresponding range in the returned
* list is marked interleaved.
*
* For example, if `b` and `m` have width 2, without the interleaved keyword,
* the returned range represents the sequence `[b0.m0, b0.m1, b1.m0, b1.m1]`.
* With the interleaved marking, the returned range represents the sequence
* `[b0.m0, b1.m0, b0.m1, b1.m1]`. Both ranges will have width 4.
*
* @param references The variable references on one side of the connection.
* @param connection The connection.
*/
private List<RuntimeRange<PortInstance>> listPortInstances(List<VarRef> references, Connection connection) {
List<RuntimeRange<PortInstance>> result = new ArrayList<RuntimeRange<PortInstance>>();
List<RuntimeRange<PortInstance>> tails = new LinkedList<RuntimeRange<PortInstance>>();
int count = 0;
for (VarRef portRef : references) {
// Simple error checking first.
if (!(portRef.getVariable() instanceof Port)) {
reporter.reportError(portRef, "Not a port.");
return result;
}
// First, figure out which reactor we are dealing with.
// The reactor we want is the container of the port.
// If the port reference has no container, then the reactor is this one.
var reactor = this;
if (portRef.getContainer() != null) {
reactor = getChildReactorInstance(portRef.getContainer());
}
// Skip this portRef so that diagram synthesis can complete.
if (reactor != null) {
PortInstance portInstance = reactor.lookupPortInstance((Port) portRef.getVariable());
Set<ReactorInstance> interleaved = new LinkedHashSet<ReactorInstance>();
if (portRef.isInterleaved()) {
// NOTE: Here, we are assuming that the interleaved()
// keyword is only allowed on the multiports contained by
// contained reactors.
interleaved.add(portInstance.parent);
}
RuntimeRange<PortInstance> range = new RuntimeRange.Port(portInstance, interleaved);
// in the hierarchy.
if (count < references.size() - 1) {
int portWidth = portInstance.width;
int portParentWidth = portInstance.parent.width;
int widthBound = portWidth * portParentWidth;
// If either of these widths cannot be determined, assume infinite.
if (portWidth < 0)
widthBound = Integer.MAX_VALUE;
if (portParentWidth < 0)
widthBound = Integer.MAX_VALUE;
if (widthBound < range.width) {
// Need to split the range.
tails.add(range.tail(widthBound));
range = range.head(widthBound);
}
}
result.add(range);
}
}
// Iterate over the tails.
while (tails.size() > 0) {
List<RuntimeRange<PortInstance>> moreTails = new LinkedList<RuntimeRange<PortInstance>>();
count = 0;
for (RuntimeRange<PortInstance> tail : tails) {
if (count < tails.size() - 1) {
int widthBound = tail.instance.width;
if (tail._interleaved.contains(tail.instance.parent)) {
widthBound = tail.instance.parent.width;
}
// If the width cannot be determined, assume infinite.
if (widthBound < 0)
widthBound = Integer.MAX_VALUE;
if (widthBound < tail.width) {
// Need to split the range again
moreTails.add(tail.tail(widthBound));
tail = tail.head(widthBound);
}
}
result.add(tail);
}
tails = moreTails;
}
return result;
}
Aggregations