Skip to content

Commit

Permalink
Add a deadline validator
Browse files Browse the repository at this point in the history
  • Loading branch information
lsk567 committed Oct 24, 2024
1 parent 0dba557 commit 12f535b
Show file tree
Hide file tree
Showing 8 changed files with 193 additions and 9 deletions.
24 changes: 22 additions & 2 deletions core/src/main/java/org/lflang/analyses/dag/Dag.java
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,26 @@ public List<DagEdge> getDagEdges() {
.collect(Collectors.toCollection(ArrayList::new));
}

/**
* Get the immediate downstream nodes of a given node.
*
* @param node the node to get the downstream nodes of
* @return a list of downstream nodes
*/
public List<DagNode> getDownstreamNodes(DagNode node) {
return new ArrayList<>(this.dagEdges.getOrDefault(node, new HashMap<>()).keySet());
}

/**
* Get the immediate upstream nodes of a given node.
*
* @param node the node to get the upstream nodes of
* @return a list of upstream nodes
*/
public List<DagNode> getUpstreamNodes(DagNode node) {
return new ArrayList<>(this.dagEdgesRev.getOrDefault(node, new HashMap<>()).keySet());
}

/**
* Sort the dag nodes by the topological order, i.e., if node B depends on node A, then A has a
* smaller index than B in the list.
Expand Down Expand Up @@ -362,8 +382,8 @@ public CodeBuilder generateDot(List<List<Instruction>> instructions) {
if (instructions != null && node.nodeType == DagNode.dagNodeType.REACTION) {
int worker = node.getWorker();
List<Instruction> workerInstructions = instructions.get(worker);
if (node.getInstructions(workerInstructions).size() > 0) label += "\\n" + "Instructions:";
for (Instruction inst : node.getInstructions(workerInstructions)) {
if (node.filterInstructions(workerInstructions).size() > 0) label += "\\n" + "Instructions:";
for (Instruction inst : node.filterInstructions(workerInstructions)) {
label += "\\n" + inst.getOpcode() + " (worker " + inst.getWorker() + ")";
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,7 @@ public Dag generateDag(StateSpaceDiagram stateSpaceDiagram) {
// Modeling the release deadline as a completion deadline.
// Completion deadline = release time + WCET + deadline value.
TimeValue deadlineTime = associatedSync.timeStep.add(reactionWcet).add(deadlineValue);
// Create and add a SYNC node inferred from the deadline.
DagNode syncNode = addSyncNodeToDag(dag, deadlineTime, syncNodesPQueue);
// Add an edge from the reaction node to the SYNC node.
dag.addEdge(reactionNode, syncNode);
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/org/lflang/analyses/dag/DagNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ public void setReleaseValue(Long value) {
* query a list of instructions owned by a node, a _view_ of workerInstructions is generated to
* collect instructions which belong to that node.
*/
public List<Instruction> getInstructions(List<Instruction> workerInstructions) {
public List<Instruction> filterInstructions(List<Instruction> workerInstructions) {
return workerInstructions.stream().filter(it -> it.getDagNode() == this).toList();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ private static void populateEquivalenceClasses(
for (int i = 0; i < equivalenceClasses.size(); i++) {
List<DagNode> list = equivalenceClasses.get(i);
DagNode listHead = list.get(0);
if (node.getInstructions(workerInstructions)
.equals(listHead.getInstructions(workerInstructions))) {
if (node.filterInstructions(workerInstructions)
.equals(listHead.filterInstructions(workerInstructions))) {
list.add(node);
matched = true;
nodeToProcedureIndexMap.put(node, i);
Expand Down Expand Up @@ -123,7 +123,7 @@ private static void factorOutProcedures(
// Get the head of the equivalence class list.
DagNode listHead = equivalenceClasses.get(procedureIndex).get(0);
// Look up the instructions in the first node in the equivalence class list.
List<Instruction> procedureCode = listHead.getInstructions(workerInstructions);
List<Instruction> procedureCode = listHead.filterInstructions(workerInstructions);

// FIXME: Factor this out.
// Remove any phase labels from the procedure code.
Expand Down Expand Up @@ -188,7 +188,7 @@ private static void factorOutProcedures(
// Get the worker instructions.
List<Instruction> workerInstructions = objectFile.getContent().get(w);
// Add instructions from this node.
updatedInstructions.get(w).addAll(node.getInstructions(workerInstructions));
updatedInstructions.get(w).addAll(node.filterInstructions(workerInstructions));
}
}
}
Expand Down
127 changes: 127 additions & 0 deletions core/src/main/java/org/lflang/analyses/opt/DeadlineValidator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package org.lflang.analyses.opt;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.lflang.MessageReporter;
import org.lflang.TimeValue;
import org.lflang.analyses.dag.Dag;
import org.lflang.analyses.dag.DagNode;
import org.lflang.analyses.pretvm.PretVmObjectFile;
import org.lflang.analyses.pretvm.instructions.Instruction;
import org.lflang.analyses.pretvm.profiles.FlexPRETProfile;
import org.lflang.analyses.statespace.StateSpaceExplorer.Phase;
import org.lflang.target.TargetConfig;
import org.lflang.target.property.PlatformProperty;
import org.lflang.target.property.PlatformProperty.PlatformOptions;
import org.lflang.target.property.SchedulerProperty;
import org.lflang.target.property.type.PlatformType.Platform;

public class DeadlineValidator {
/**
* Validate the deadlines in the DAG of the given PretVM object file.
* @param messageReporter The message reporter to report any errors/warnings to.
* @param targetConfig The target configuration.
* @param objectFile The PretVM object file to validate.
* @return True if the deadlines are met, false otherwise.
*/
public static boolean validateDeadline(MessageReporter messageReporter, TargetConfig targetConfig, PretVmObjectFile objectFile) {
// Get the phase from the object file.
Phase phase = objectFile.getFragment().getPhase();
// Get the DAG from the object file
Dag dag = objectFile.getDag();
// Map a node to a makespan up to that node
Map<DagNode, TimeValue> makespan = new HashMap<DagNode, TimeValue>();
// Flag to indicate if the deadline is met
boolean deadlineMet = true;
// Perform a topological sort of the DAG and calculate the makespan.
for (DagNode node : dag.getTopologicalSort()) {
// The head node must be a SYNC node, so the makespan is 0.
if (node == dag.head) {
makespan.put(node, TimeValue.ZERO);
continue;
}
// Look up the makespan of the predecessors of the node.
List<DagNode> upstreamNodes = dag.getUpstreamNodes(node);
TimeValue maxUpstreamMakespan = upstreamNodes.stream().map(it -> makespan.get(it)).max(TimeValue::compareTo).get();

// Update the makespan map based on the node type.
switch (node.nodeType) {
case DUMMY:
// FIXME: The DUMMY node's WCET is stored in the
// timeStep field. This is very ugly and need to be
// refactored.
makespan.put(node, maxUpstreamMakespan.add(node.timeStep));
break;
case SYNC:
// A SYNC node has a WCET of 0, so just add the max
// upstream makespan.
makespan.put(node, maxUpstreamMakespan);
break;
case REACTION:
// If the node is a reaction, add the reaction WCET
// to the makespan.
// Currently, we only support a single WCET per node.
if (node.nodeReaction.wcets.size() > 1) {
messageReporter.nowhere().warning("DeadlineValidator: Node " + node + " has more than one WCET.");
}
TimeValue makespanUntilNode = maxUpstreamMakespan.add(node.nodeReaction.wcets.get(0));
// For each PretVM instructions generated by the
// node, add up their overhead.
// Find all instructions own by the node's worker.
List<Instruction> workInsts = objectFile.getContent().get(node.getWorker());
// Find the instructions that belong to this node only.
List<Instruction> nodeInsts = node.filterInstructions(workInsts);
// Add up the overhead of the instructions.
Platform platform = targetConfig.getOrDefault(PlatformProperty.INSTANCE).platform();
// Add instruction overhead based on the platform used.
if (platform != null) {
switch (platform) {
case AUTO: break;
case FLEXPRET:
for (Instruction inst : nodeInsts) {
makespanUntilNode = makespanUntilNode.add(FlexPRETProfile.getInstWCET(inst.getOpcode()));
}
break;
default:
messageReporter.nowhere().error("DeadlineValidator: Unknown platform " + platform);
}
}
makespan.put(node, makespanUntilNode);
break;
default:
messageReporter.nowhere().error("DeadlineValidator: Unknown node type " + node.nodeType);
return false;
}

// If a SYNC node is encountered, check if the makespan
// exceeds the deadline.
// At the moment, TimeValue has a "saturation" semantics,
// meaning that if the sum of two time values exceeds the
// maximum time value, the sum becomes the maximum time
// value. This semantics helps with deadline checking here,
// If any reaction has an unknown WCET
// (represented as TimeValue.MAX_VALUE), this pulls up the
// makespan along the DAG node chain to TimeValue.MAX_VALUE.
// For real deadlines, i.e., SYNC nodes with timestamp <
// TimeValue.MAX_VALUE, deadline violations can be easily
// detected using the compareTo() method. For fake
// deadlines, SYNC nodes with timestamp ==
// TimeValue.MAX_VALUE (these SYNC nodes are simply there to
// represent the end of a hyperperiod / phase),
// the saturation semantics make sure that compareTo()
// returns 0 and no deadline violations are returned.
if (node.nodeType == DagNode.dagNodeType.SYNC) {
if (makespan.get(node).compareTo(node.timeStep) > 0) {
messageReporter.nowhere().warning("DeadlineValidator: Deadline violation detected in phase " + phase + " at node " + node);
deadlineMet = false;
}
}

// FIXME: Generate a DOT file that shows the makespan.
}

return deadlineMet;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.lflang.analyses.pretvm.profiles;

import org.lflang.TimeValue;
import org.lflang.analyses.pretvm.instructions.Instruction.Opcode;

public record FlexPRETProfile() {
// FIXME: This is a placeholder for the FlexPRET profile. The actual
// values should be determined experimentally.
public static TimeValue getInstWCET(Opcode opcode) {
return switch (opcode) {
case ADD -> TimeValue.fromNanoSeconds(1000L);
case ADDI -> TimeValue.fromNanoSeconds(1000L);
case ADV -> TimeValue.fromNanoSeconds(1000L);
case ADVI -> TimeValue.fromNanoSeconds(1000L);
case BEQ -> TimeValue.fromNanoSeconds(1000L);
case BGE -> TimeValue.fromNanoSeconds(1000L);
case BLT -> TimeValue.fromNanoSeconds(1000L);
case BNE -> TimeValue.fromNanoSeconds(1000L);
case DU -> TimeValue.fromNanoSeconds(1000L);
case EXE -> TimeValue.fromNanoSeconds(1000L);
case JAL -> TimeValue.fromNanoSeconds(1000L);
case JALR -> TimeValue.fromNanoSeconds(1000L);
case STP -> TimeValue.fromNanoSeconds(1000L);
case WLT -> TimeValue.fromNanoSeconds(1000L);
case WU -> TimeValue.fromNanoSeconds(1000L);
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.lflang.analyses.dag.Dag;
import org.lflang.analyses.dag.DagGenerator;
import org.lflang.analyses.opt.DagBasedOptimizer;
import org.lflang.analyses.opt.DeadlineValidator;
import org.lflang.analyses.opt.PeepholeOptimizer;
import org.lflang.analyses.pretvm.InstructionGenerator;
import org.lflang.analyses.pretvm.PretVmExecutable;
Expand Down Expand Up @@ -187,6 +188,9 @@ public void generate() {
// Generate instructions (wrapped in an object file) from DAG partitions.
PretVmObjectFile objectFile = instGen.generateInstructions(dagPartitioned, fragment);

// Check if deadlines could be violated.
DeadlineValidator.validateDeadline(messageReporter, targetConfig, objectFile);

// Point the fragment to the new object file.
fragment.setObjectFile(objectFile);

Expand Down
8 changes: 6 additions & 2 deletions test/C/src/static/test/StaticDeadline.lf
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ target C {
mapper: LB
},
timeout: 6 sec,
workers: 1
workers: 1,
// platform: FlexPRET,
}

preamble {=
Expand All @@ -24,7 +25,10 @@ reactor Source(period: time = 3 sec) {
timer t(0, period)
state count: int = 0

@wcet("1 ms")
// If FlexPRET is used, a 1-sec WCET violates the deadline due to the
// additional runtime overhead. Set the WCET to 999 ms to avoid the
// violation.
@wcet("1 s")
reaction(t) -> y {=
if (2 * (self->count / 2) != self->count) {
// The count variable is odd.
Expand Down

0 comments on commit 12f535b

Please sign in to comment.