Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for cross connections #416

Merged
merged 20 commits into from
Jul 29, 2021
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
b02c8df
grammar: add syntax for cross connections
cmnrd Jul 20, 2021
56b65c3
cpp: generate code for cross connections
cmnrd Jul 20, 2021
868be3f
cpp: bugfix, generate compilable code for cross connections
cmnrd Jul 20, 2021
d06e969
cpp: add patterns highlighting the cross connection feature
cmnrd Jul 20, 2021
b9c9e8f
cpp: add a testcase for cross connections
cmnrd Jul 20, 2021
faacfba
Suppose cross connections in the C target. Also, redesigned and simpl…
edwardalee Jul 22, 2021
bf6e427
Removed testing code from examples for better code clarity
edwardalee Jul 22, 2021
242990a
grammar: update grammar to use interleaved qualifiers on references
cmnrd Jul 28, 2021
358739e
grammar: change to a more permissive grammar rule
cmnrd Jul 28, 2021
268b903
validator: fix code and add checks for VarRef
cmnrd Jul 28, 2021
6a377ec
c, cpp: fix examples and tests
cmnrd Jul 28, 2021
a8ef77d
cpp: disambiguate the dual use of receive and further fixes
cmnrd Jul 28, 2021
8301a07
cpp: rename pattern example to follow C naming scheme
cmnrd Jul 28, 2021
fbf0a23
cpp: test interleaving on left and right side of the connection
cmnrd Jul 28, 2021
480a3e1
c, cpp: add tests for interleaved in combination with after
cmnrd Jul 28, 2021
a540bf5
Merge branch 'master' into cross-connections
cmnrd Jul 28, 2021
37d8be2
small fixes as suggested by @edwardlee
cmnrd Jul 28, 2021
6df14f7
Updated to new bank_index parameter, which has been pushed to master
edwardalee Jul 28, 2021
020afec
Renamed test to fix spelling error
edwardalee Jul 28, 2021
f03f472
also fix use of bank_index in C patterns
cmnrd Jul 28, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion example/C/src/Patterns/FullyConnected_01_Addressable.lf
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,5 @@ reactor Node(num_nodes: size_t(4)) {

main reactor(num_nodes: size_t(4)) {
nodes = new[num_nodes] Node(num_nodes=num_nodes);
nodes.out -x-> nodes.in;
nodes.out -> interleaved(nodes.in);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
// In this pattern, each node can send broadcast messages to all other nodes
/**
* This illustrates bank of reactors where each reactor produces an
* output that is broadcast to all reactors in the bank, including
* itself.
*
* @author Christian Menard
* @author Edward A. Lee
*/

target Cpp {
threads: 1
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
// In this pattern, each node can send direct messages to individual other nodes
/**
* This illustrates bank of reactors where each reactor produces an
* output that is sent to a reactor in the bank of its choice. In this
* particular example, each reactor chooses to send its output to the
* reactor with the next higher bank_index, wrapping around when it gets
* to the end of the bank.
*
* @author Christian Menard
* @author Edward A. Lee
*/

target Cpp {
threads: 1
Expand Down Expand Up @@ -27,5 +36,5 @@ reactor Node(bank_index: size_t(0), num_nodes: size_t(4)) {

main reactor(num_nodes: size_t(4)) {
nodes = new[num_nodes] Node(num_nodes=num_nodes);
(nodes.out)+ -x-> nodes.in;
nodes.out -> interleaved(nodes.in);
}
9 changes: 5 additions & 4 deletions org.lflang/src/org/lflang/LinguaFranca.xtext
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ Instantiation:
Connection:
((leftPorts += VarRef (',' leftPorts += VarRef)*)
| ( '(' leftPorts += VarRef (',' leftPorts += VarRef)* ')' isIterated ?= '+'?))
('->' | physical?='~>' | cross?='-x->')
('->' | physical?='~>')
rightPorts += VarRef (',' rightPorts += VarRef)*
(delay=Delay | ';'?);

Expand Down Expand Up @@ -274,10 +274,10 @@ Trigger:

Effect:
Action | Output;

VarRef:
variable=[Variable]
| container=[Instantiation] '.' variable=[Variable];
variable=[Variable] | container=[Instantiation] '.' variable=[Variable]
| interleaved?='interleaved' '(' (variable=[Variable] | container=[Instantiation] '.' variable=[Variable]) ')'
;

Assignment:
(lhs=[Parameter] (
Expand Down Expand Up @@ -482,6 +482,7 @@ Token:
'mutable' | 'input' | 'output' | 'timer' | 'action' | 'reaction' |
'startup' | 'shutdown' | 'after' | 'deadline' | 'mutation' | 'preamble' |
'new' | 'federated' | 'at' | 'as' | 'from' | 'widthof' | 'const' | 'method' |
'interleaved' |
// Other terminals
NEGINT | TRUE | FALSE |
// Action origins
Expand Down
16 changes: 8 additions & 8 deletions org.lflang/src/org/lflang/generator/ReactorInstance.xtend
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,8 @@ class ReactorInstance extends NamedInstance<Instantiation> {
*/
def establishPortConnections() {
for (connection : reactorDefinition.allConnections) {
val leftPortInstances = listPortInstances(connection.leftPorts, false)
val rightPortInstances = listPortInstances(connection.rightPorts, connection.cross)
val leftPortInstances = listPortInstances(connection.leftPorts)
val rightPortInstances = listPortInstances(connection.rightPorts)

// Check widths.
if (leftPortInstances.size > rightPortInstances.size) {
Expand Down Expand Up @@ -147,17 +147,17 @@ class ReactorInstance extends NamedInstance<Instantiation> {
* If the port reference has the form `c.x`, where `c` is a bank of reactors,
* then the list will contain the port instances belonging to each bank member.
*
* If the cross argument is false, then for any port reference `b.m` where `b`
* is a bank and `m` is a multiport, this function iterates over bank members first,
* If a given port reference `b.m`. where `b` is a bank and `m` is a multiport,
cmnrd marked this conversation as resolved.
Show resolved Hide resolved
* is unqualified, this function iterates over bank members first,
* then ports. E.g., if `b` and `m` have width 2, it returns
* `[b0.m0, b0.m1, b1.m0, b1.m1]`.
*
* If the cross argument is true, then for any port reference `b.m` where `b`
* is a bank and `m` is a multiport, this function iterates over ports first,
* If a given port reference `b.m`. where `b` is a bank and `m` is a multiport,
* is qualified with 'interleaved', this function iterates over ports first,
cmnrd marked this conversation as resolved.
Show resolved Hide resolved
* then bank members. E.g., if `b` and `m` have width 2, it returns
* `[b0.m0, b1.m0, b0.m1, b1.m1]`.
*/
private def List<PortInstance> listPortInstances(List<VarRef> references, boolean cross) {
private def List<PortInstance> listPortInstances(List<VarRef> references) {
val result = new LinkedList<PortInstance>();
for (portRef : references) {
// Simple error checking first.
Expand All @@ -178,7 +178,7 @@ class ReactorInstance extends NamedInstance<Instantiation> {
if (reactor !== null) {
if (reactor.bankMembers !== null) {
// Reactor is a bank.
if (!cross) {
if (!portRef.isInterleaved) {
// Not a cross connection.
for (memberReactor: reactor.bankMembers) {
val portInstance = memberReactor.lookupPortInstance(portRef.variable as Port)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,14 @@ class CppAssembleMethodGenerator(private val reactor: Reactor) {

private fun iterateOverAllPortsAndApply(
varRef: VarRef,
iteratePortsFirst: Boolean = false,
generateCode: (String) -> String
): String {
val port = varRef.variable as Port
val container = varRef.container
return with(PrependOperator) {
if (port.isMultiport) {
if (container?.isBank == true) {
if (iteratePortsFirst) {
if (varRef.isInterleaved) {
"""
|for (size_t __lf_port_idx = 0; __lf_port_idx < ${container.name}[0]->${port.name}.size(); __lf_port_idx++) {
| for (auto& __lf_instance : ${container.name}) {
Expand Down Expand Up @@ -154,16 +153,16 @@ class CppAssembleMethodGenerator(private val reactor: Reactor) {
}

private fun declareConnection(c: Connection, idx: Int): String {
if (c.isMultiportConnection) {
return declareMultiportConnection(c, idx);
return if (c.isMultiportConnection) {
declareMultiportConnection(c, idx)
} else {
val leftPort = c.leftPorts[0]
val rightPort = c.rightPorts[0]

return """
// connection $idx
${leftPort.name}.bind_to(&${rightPort.name});
""".trimIndent()
"""
// connection $idx
${leftPort.name}.bind_to(&${rightPort.name});
""".trimIndent()
}
}

Expand Down Expand Up @@ -198,14 +197,14 @@ class CppAssembleMethodGenerator(private val reactor: Reactor) {
|std::vector<$portType> __lf_left_ports_$idx;
${" |"..c.leftPorts.joinToString("\n") { addAllPortsToVector(it, "__lf_left_ports_$idx") }}
|std::vector<$portType> __lf_right_ports_$idx;
${" |"..c.rightPorts.joinToString("\n") { addAllPortsToVector(it, "__lf_right_ports_$idx", c.isCross) }}
${" |"..c.rightPorts.joinToString("\n") { addAllPortsToVector(it, "__lf_right_ports_$idx") }}
|lfutil::bind_multiple_ports(__lf_left_ports_$idx, __lf_right_ports_$idx, ${c.isIsIterated});
""".trimMargin()
}
}

private fun addAllPortsToVector(varRef: VarRef, vectorName: String, iteratePortsFirst: Boolean = false): String =
iterateOverAllPortsAndApply(varRef, iteratePortsFirst) { port: String -> "${vectorName}.push_back(&$port);" }
private fun addAllPortsToVector(varRef: VarRef, vectorName: String): String =
iterateOverAllPortsAndApply(varRef) { port: String -> "${vectorName}.push_back(&$port);" }

/**
* Generate the definition of the reactor's assemble() method
Expand Down
25 changes: 17 additions & 8 deletions org.lflang/src/org/lflang/validation/LFValidatorImpl.xtend
Original file line number Diff line number Diff line change
Expand Up @@ -345,14 +345,6 @@ class LFValidatorImpl extends AbstractLFValidator {

@Check(FAST)
def checkConnection(Connection connection) {

if (connection.cross
&& this.target != Target.CPP
&& this.target != Target.C
&& this.target != Target.Python
) {
error("This target does not support cross connections", Literals.CONNECTION__CROSS)
}

// Report if connection is part of a cycle.
for (cycle : this.info.topologyGraph.cycles) {
Expand Down Expand Up @@ -1292,6 +1284,23 @@ class LFValidatorImpl extends AbstractLFValidator {
}
}
}

@Check(FAST)
def checkVarRef(VarRef varRef) {
if (varRef.isInterleaved) {
if (this.target != Target.CPP && this.target != Target.C && this.target != Target.Python) {
error("This target does not support interleaved port referenced", Literals.VAR_REF__INTERLEAVED)
cmnrd marked this conversation as resolved.
Show resolved Hide resolved
}
if (varRef.container === null || varRef.container.widthSpec === null ||
(varRef.variable as Port).widthSpec === null
) {
error("Interleaved can only be used for multiports contained within banks", Literals.VAR_REF__INTERLEAVED)
cmnrd marked this conversation as resolved.
Show resolved Hide resolved
}
if (!(varRef.eContainer instanceof Connection)) {
error("Interleaved can only be used in connections", Literals.VAR_REF__INTERLEAVED)
cmnrd marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

static val UNDERSCORE_MESSAGE = "Names of objects (inputs, outputs, actions, timers, parameters, state, reactor definitions, and reactor instantiation) may not start with \"__\": "
static val ACTIONS_MESSAGE = "\"actions\" is a reserved word for the TypeScript target for objects (inputs, outputs, actions, timers, parameters, state, reactor definitions, and reactor instantiation): "
Expand Down
7 changes: 5 additions & 2 deletions test/C/src/multiport/FullyConnectedAdressable.lf
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ reactor Node(num_nodes: size_t(4)) {
}

main reactor(num_nodes: size_t(4)) {
nodes = new[num_nodes] Node(num_nodes=num_nodes);
nodes.out -x-> nodes.in;
nodes1 = new[num_nodes] Node(num_nodes=num_nodes);
nodes1.out -> interleaved(nodes1.in);

nodes2 = new[num_nodes] Node(num_nodes=num_nodes);
interleaved(nodes2.out) -> nodes2.in;
}
13 changes: 13 additions & 0 deletions test/C/src/multiport/FullyConnectedAdressableAfter.lf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// In this pattern, each node can send direct messages to individual other nodes

target C;

import Node from "FullyConnectedAdressable.lf"

main reactor(num_nodes: size_t(4)) {
nodes1 = new[num_nodes] Node(num_nodes=num_nodes);
nodes1.out -> interleaved(nodes1.in) after 200msec;

nodes2 = new[num_nodes] Node(num_nodes=num_nodes);
interleaved(nodes2.out) -> nodes2.in after 400msec;
}
21 changes: 13 additions & 8 deletions test/Cpp/src/multiport/FullyConnectedAdressable.lf
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,22 @@ reactor Node(bank_index: size_t(0), num_nodes: size_t(4)) {
std::cout << "Node " << bank_index << " received messages from ";
received = true;
size_t count{0};
size_t received{0};
size_t result{0};
for (auto& port : in) {
if (port.is_present()) {
count++;
received = *port.get();
std::cout << received << ", ";
result = *port.get();
std::cout << result << ", ";
}
}
std::cout << '\n';

size_t expected = bank_index == 0 ? num_nodes - 1 : bank_index - 1;
if (count != 1 || received != expected) {
std::cerr << "ERROR: received an unexpected message!";
if (count != 1 || result != expected) {
std::cerr << "ERROR: received an unexpected message!\n";
exit(1);
}
std::cout << '\n';

=}
reaction (shutdown) {=
if (!received) {
Expand All @@ -44,6 +46,9 @@ reactor Node(bank_index: size_t(0), num_nodes: size_t(4)) {
}

main reactor(num_nodes: size_t(4)) {
nodes = new[num_nodes] Node(num_nodes=num_nodes);
(nodes.out)+ -x-> nodes.in;
nodes1 = new[num_nodes] Node(num_nodes=num_nodes);
nodes1.out -> interleaved(nodes1.in);

nodes2 = new[num_nodes] Node(num_nodes=num_nodes);
interleaved(nodes2.out) -> nodes2.in;
}
15 changes: 15 additions & 0 deletions test/Cpp/src/multiport/FullyConnectedAdressableAfter.lf
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// In this pattern, each node can send direct messages to individual other nodes
cmnrd marked this conversation as resolved.
Show resolved Hide resolved

target Cpp {
threads: 1
}

import Node from "FullyConnectedAdressable.lf"

main reactor(num_nodes: size_t(4)) {
nodes1 = new[num_nodes] Node(num_nodes=num_nodes);
nodes1.out -> interleaved(nodes1.in) after 200msec;

nodes2 = new[num_nodes] Node(num_nodes=num_nodes);
interleaved(nodes2.out) -> nodes2.in after 400msec;
}