/* * Copyright (c) 2016, Heriot-Watt University * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of the IETR/INSA of Rennes nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ package net.sf.orcc.xdf.ui.features; import net.sf.orcc.xdf.ui.patterns.InstancePattern; import net.sf.orcc.xdf.ui.styles.StyleUtil; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import net.sf.orcc.df.Instance; import net.sf.orcc.df.Network; import net.sf.orcc.graph.Vertex; import net.sf.orcc.xdf.ui.util.PropsUtil; import net.sf.orcc.xdf.ui.util.XdfUtil; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.emf.common.util.EList; import org.eclipse.graphiti.features.IFeatureProvider; import org.eclipse.graphiti.features.context.IContext; import org.eclipse.graphiti.features.context.ICustomContext; import org.eclipse.graphiti.mm.algorithms.GraphicsAlgorithm; import org.eclipse.graphiti.mm.algorithms.Polyline; import org.eclipse.graphiti.mm.algorithms.RoundedRectangle; import org.eclipse.graphiti.mm.algorithms.Text; import org.eclipse.graphiti.mm.pictograms.Diagram; import org.eclipse.graphiti.mm.pictograms.PictogramElement; import org.eclipse.graphiti.mm.pictograms.Shape; import org.eclipse.graphiti.services.Graphiti; import org.eclipse.graphiti.services.IGaService; import org.eclipse.graphiti.ui.services.GraphitiUi; import org.eclipse.swt.widgets.DirectoryDialog; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; /** * This class reads BRAM and maximum gate depth costs that is computed at * compile time with the Xronos backend that generates Verilog. It parses HTML * and XML files in the rtl/report/ directory created by Orcc when this backend * is used. These costs are then highlighted on the XDF dataflow diagram. It * details the number of BRAM blocks needed to implement the actor, and also the * maximum gate depth by naming the action with the largest gate depth. The * container shapes corresponding to actors with relatively larger gate depths * become yellow boxes, and actor with relative lower gate depths become green * boxes. The actor contributing to the combintional critical path (i.e. which * actor is determining the clock frequency) is indicated with an orange box. * * @author Rob Stewart * */ public class XronosProfileFeature extends AbstractTimeConsumingCustomFeature { private String xronosReportsDir; private int COST_SPACE = 40; public XronosProfileFeature(IFeatureProvider fp) { super(fp); } @Override public String getName() { return "Xronos FPGA costs"; } @Override public String getDescription() { return "Highlight FPGA costs from Xronos"; } @Override public boolean isAvailable(IContext context) { return super.isAvailable(context); } @Override /** * lets the user click this highlighter if they have right clicked white * space in the XDF network, and does not allow this highlighter to be used * if they have right clicked any other graphical feature in the XDF * interface. */ public boolean canExecute(ICustomContext context) { boolean canExec = false; PictogramElement[] pes = context.getPictogramElements(); if (pes.length == 1) { final PictogramElement pe = pes[0]; if (getBusinessObjectForPictogramElement(pe) instanceof Network) { canExec = true; } } return canExec; } @Override /** * Uses a directory dialog box for the user to locate the rtl/report/ * directory generated from the Xronos backend by compiling the XDF network. */ protected void beforeJobExecution() { DirectoryDialog dialog = new DirectoryDialog(XdfUtil.getDefaultShell()); dialog.setMessage("Select the rtl/report/ director"); dialog.setText("Select the rtl/report/ directory"); String selectedDirectoryName = dialog.open(); xronosReportsDir = selectedDirectoryName; } @Override public void execute(ICustomContext context, IProgressMonitor parentMonitor) { final Network currentNetwork = (Network) getBusinessObjectForPictogramElement(getDiagram()); Map<String, InstanceCosts> instanceCosts = new HashMap<String, InstanceCosts>(); /* * obtain max gate depth and BRAM costs for each instance by parsing XML * and HMTL files */ List<Vertex> nodes = currentNetwork.getVertices(); List<Integer> maxDepths = new ArrayList<Integer>(); for (Vertex vertex : nodes) { if (vertex instanceof Instance) { Instance inst = (Instance) vertex; /* test that costs for the instance is is rtl/report/ */ String reportFile = xronosReportsDir + "/" + inst.getName() + "_ResourceUtilizationReport.html"; File f = new File(reportFile); if (f.exists() && !f.isDirectory()) { /* parse the XML file corresponding to the instance */ MaxGateDepth maxGateDepth = maxGateDepth(inst.getName()); maxDepths.add(maxGateDepth.getMaxDepth()); /* * parse the ResourceUtilizationReport.html corresponding to * the instance */ int brams = brams(inst.getName()); instanceCosts.put(inst.getName(), new InstanceCosts(maxGateDepth, brams)); } } } /* sort the maximum gate depths to find relative costs */ Collections.sort(maxDepths); /* split them into a lower half and upper half list */ List<Integer> upperHalf; Integer highest; if (maxDepths.size() < 1) { upperHalf = new ArrayList<Integer>(); highest = 0; } else { upperHalf = maxDepths.subList(maxDepths.size() / 2, maxDepths.size() - 1); highest = maxDepths.get(maxDepths.size() - 1); } /* * Add the relative maximum gate depth cost to each InstanceCosts * instance */ for (Map.Entry<String, InstanceCosts> entry : instanceCosts.entrySet()) { if (highest.equals(new Integer(entry.getValue().getMaxGateDepth().maxDepth))) { InstanceCosts ic = entry.getValue(); ic.setRelativeDepthCost(RelativeDepthCost.HIGHEST); entry.setValue(ic); } else if (upperHalf.contains(new Integer(entry.getValue().getMaxGateDepth().maxDepth))) { InstanceCosts ic = entry.getValue(); ic.setRelativeDepthCost(RelativeDepthCost.HIGH); entry.setValue(ic); } else { // lower half InstanceCosts ic = entry.getValue(); ic.setRelativeDepthCost(RelativeDepthCost.LOW); entry.setValue(ic); } } /* highlight the XDF diagram with the parsed and computed costs */ overlayCosts(getDiagram(), instanceCosts); } /** * Overlays the BRAM and max gate depth costs to each instance in the XDF * diagram * * @param diagram * The XDF diagram * @param instanceCosts * The costs parsed from HTML and XML files in rtl/report/ */ private void overlayCosts(Diagram diagram, Map<String, InstanceCosts> instanceCosts) { IGaService service = GraphitiUi.getGaService(); EList<Shape> shapes = diagram.getChildren(); for (Shape shape : shapes) { GraphicsAlgorithm grAlg = shape.getGraphicsAlgorithm(); if (grAlg instanceof RoundedRectangle) { RoundedRectangle roundRect = (RoundedRectangle) grAlg; /* get the instance name */ GraphicsAlgorithm gr = roundRect.getGraphicsAlgorithmChildren().get(0); String instanceName = ((Text) gr).getValue(); /* * only modify a rectangle box if costs are known for the * instance, i.e. if there were corresponding HTML and XML files * in rtl/report/ for the instance. */ if (instanceCosts.containsKey(instanceName)) { int rectHeight = roundRect.getHeight(); /* * if the XDF network has already been highlighted once with * Xronos costs, then we first need to remove the four GUI * features that get added to the rectangle corresponding to * the instance. */ Iterator<GraphicsAlgorithm> gaIter = roundRect.getGraphicsAlgorithmChildren().iterator(); while (gaIter.hasNext()) { GraphicsAlgorithm ga = gaIter.next(); if (ga instanceof Text) { Text tmp = (Text) ga; if (Graphiti.getPeService().getProperty(tmp, "XDF_ID") != null) { if (Graphiti.getPeService().getProperty(tmp, "XDF_ID").getValue() .equals("maxDepth-lbl")) { gaIter.remove(); /* also reduce the height of the box */ roundRect.setHeight(rectHeight - COST_SPACE); } } else if (Graphiti.getPeService().getProperty(tmp, "XDF_ID") != null) { if (Graphiti.getPeService().getProperty(tmp, "XDF_ID").getValue().equals("bram-lbl")) { gaIter.remove(); } } else if (Graphiti.getPeService().getProperty(tmp, "XDF_ID") != null) { if (Graphiti.getPeService().getProperty(tmp, "XDF_ID").getValue() .equals("maxDepthAction-lbl")) { gaIter.remove(); } } else if (Graphiti.getPeService().getProperty(tmp, "XDF_ID") != null) { if (Graphiti.getPeService().getProperty(tmp, "XDF_ID").getValue() .equals("hardware-costs-separator")) { gaIter.remove(); } } } } /* look up the costs for the instance */ InstanceCosts costs = instanceCosts.get(instanceName); /* * max gate depths have been ordered: low, high or highest. * Low relative gate depths go green, high relative depths * go yellow, whilst the highest relative gate depth does * orange. */ switch (costs.getRelativeDepthCost()) { case LOW: shape.getGraphicsAlgorithm().setStyle(StyleUtil.actorInstanceShapeGreen(diagram)); break; case HIGH: shape.getGraphicsAlgorithm().setStyle(StyleUtil.actorInstanceShapeYellow(diagram)); break; case HIGHEST: shape.getGraphicsAlgorithm().setStyle(StyleUtil.actorInstanceShapeOrange(diagram)); break; } /* make space in the rectangle for the cost information */ rectHeight = roundRect.getHeight(); roundRect.setHeight(rectHeight + COST_SPACE); /* a line that separators port info from cost info */ final int[] xy = { 0, rectHeight, InstancePattern.TOTAL_MIN_WIDTH, rectHeight }; final Polyline line = service.createPlainPolyline(roundRect, xy); PropsUtil.setIdentifier(line, "hardware-costs-separator"); line.setLineWidth(1); /* BRAM info */ final Text bramText = service.createPlainText(roundRect); PropsUtil.setIdentifier(bramText, "bram-lbl"); bramText.setStyle(StyleUtil.costsText(getDiagram())); service.setLocationAndSize(bramText, 0, rectHeight + 5, InstancePattern.TOTAL_MIN_WIDTH, 10); String suffix = costs.getBrams() < 2 ? "" : "s"; bramText.setValue(costs.getBrams() + " BRAM" + suffix); /* max gate depth number */ final Text maxDepthText = service.createPlainText(roundRect); PropsUtil.setIdentifier(maxDepthText, "maxDepth-lbl"); maxDepthText.setStyle(StyleUtil.costsText(getDiagram())); service.setLocationAndSize(maxDepthText, 0, rectHeight + 15, InstancePattern.TOTAL_MIN_WIDTH, 10); maxDepthText.setValue(costs.getMaxGateDepth().getMaxDepth() + " gate depth:"); /* max gate depth action */ final Text maxDepthActionText = service.createPlainText(roundRect); PropsUtil.setIdentifier(maxDepthActionText, "maxDepthAction-lbl"); maxDepthActionText.setStyle(StyleUtil.costsText(getDiagram())); service.setLocationAndSize(maxDepthActionText, 0, rectHeight + 25, InstancePattern.TOTAL_MIN_WIDTH, 10); maxDepthActionText.setValue(" " + costs.getMaxGateDepth().getActionName()); } } } } /** * * @param instanceName * The name of the instance of an actor * @return The total number of BRAMs for the actor, which is the sum of * BRAMs required for each action and also for global variables * (i.e. CAL arrays). */ private int brams(String instanceName) { String reportFile = xronosReportsDir + "/" + instanceName + "_ResourceUtilizationReport.html"; String prefix = "Number of Block Rams:"; int bram = 0; try { /* parse each line for this standard lexical structure */ BufferedReader br = new BufferedReader(new FileReader(reportFile)); String line = null; while ((line = br.readLine()) != null) { if (line.startsWith(prefix)) { bram += parseBramLine(line); } } br.close(); } catch (IOException e) { e.printStackTrace(); } return bram; } private int parseBramLine(String line){ String[] ss = line.split("Number of Block Rams:"); String s = ss[1]; while (s.charAt(0) == ' ' ) { s = s.substring(1, s.length()); } return Integer.parseInt(s); } /** * * @param instanceName * The name of the instance of an actor * @return The maximum gate depth in MaxGateDepth, which holds both the * maximum gate depth for the actor, and also the name of the action * responsible for that gate depth. */ private MaxGateDepth maxGateDepth(String instanceName) { MaxGateDepth maxDepth = new MaxGateDepth(); String reportFile = xronosReportsDir + "/" + instanceName + ".xml"; File fXmlFile = new File(reportFile); DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder dBuilder; try { dBuilder = dbFactory.newDocumentBuilder(); Document doc = dBuilder.parse(fXmlFile); doc.getDocumentElement().normalize(); NodeList actions = doc.getElementsByTagName("Task"); for (int i = 0; i < actions.getLength(); i++) { Node action = actions.item(i); if (action.getNodeType() == Node.ELEMENT_NODE) { Element actionElement = (Element) action; NodeList resourceList = actionElement.getElementsByTagName("Resource"); Node resource = resourceList.item(0); if (resource.getNodeType() == Node.ELEMENT_NODE) { Element resourceElement = (Element) resource; int depth = Integer.parseInt(resourceElement.getAttribute("MaxGateDepth")); if (depth > maxDepth.getMaxDepth()) { maxDepth.setMaxDepth(depth, actionElement.getAttribute("name")); } } } } } catch (ParserConfigurationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SAXException e) { // TODO Auto-generated catch block e.printStackTrace(); } return maxDepth; } public enum RelativeDepthCost { LOW, HIGH, HIGHEST } /* * The maximum gate depth is held in MaxGateDepth instances, which holds * both the maximum gate depth for the actor, and also the name of the * action responsible for that gate depth. */ private class MaxGateDepth { int maxDepth; String actionName; public MaxGateDepth() { maxDepth = 0; actionName = ""; } void setMaxDepth(int depth, String action) { maxDepth = depth; actionName = action; } int getMaxDepth() { return maxDepth; } String getActionName() { return actionName; } } /* * This class holds all information about the BRAM and maximum gate depth * costs for an instance of an actor. The costs are parsed from HTML and XML * files generated by the Xronos backend in rtl/report/ . */ class InstanceCosts { MaxGateDepth gateDepth; int brams; RelativeDepthCost relativeCost; public InstanceCosts(MaxGateDepth gateDepths, int brams) { this.gateDepth = gateDepths; this.brams = brams; this.relativeCost = RelativeDepthCost.LOW; } public void setRelativeDepthCost(RelativeDepthCost cost) { this.relativeCost = cost; } public MaxGateDepth getMaxGateDepth() { return gateDepth; } public int getBrams() { return brams; } public RelativeDepthCost getRelativeDepthCost() { return relativeCost; } } }