/* * Copyright (c) 2012, IRISA * 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 IRISA 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.backends.llvm.tta.architecture.util; import static net.sf.orcc.OrccLaunchConstants.DEFAULT_FIFO_SIZE; import static net.sf.orcc.OrccLaunchConstants.FIFO_SIZE; import static net.sf.orcc.backends.BackendsConstants.TTA_CONNECTION_REDUCTION; import static net.sf.orcc.backends.BackendsConstants.TTA_CONNECTION_REDUCTION_DEFAULT; import static net.sf.orcc.backends.BackendsConstants.TTA_DEFAULT_PROCESSORS_CONFIGURATION; import static net.sf.orcc.backends.BackendsConstants.TTA_PROCESSORS_CONFIGURATION; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import net.sf.orcc.OrccRuntimeException; import net.sf.orcc.backends.llvm.tta.architecture.ArchitectureFactory; import net.sf.orcc.backends.llvm.tta.architecture.Component; import net.sf.orcc.backends.llvm.tta.architecture.Design; import net.sf.orcc.backends.llvm.tta.architecture.FunctionUnit; import net.sf.orcc.backends.llvm.tta.architecture.Memory; import net.sf.orcc.backends.llvm.tta.architecture.Port; import net.sf.orcc.backends.llvm.tta.architecture.Processor; import net.sf.orcc.backends.llvm.tta.architecture.ProcessorConfiguration; import net.sf.orcc.backends.llvm.tta.architecture.Signal; import net.sf.orcc.backends.util.Mapping; import net.sf.orcc.df.Actor; import net.sf.orcc.df.Argument; import net.sf.orcc.df.Connection; import net.sf.orcc.df.Instance; import net.sf.orcc.df.Network; import net.sf.orcc.df.util.DfSwitch; import net.sf.orcc.graph.Vertex; import net.sf.orcc.ir.Var; import net.sf.orcc.util.OrccLogger; /** * This class contains several methods to build a hardware design. * * @author Herve Yviquel * */ public class ArchitectureBuilder extends DfSwitch<Design> { private int bufferId; private int signalId; private Map<Vertex, Component> componentMap; private Design design; private ArchitectureFactory factory = ArchitectureFactory.eINSTANCE; private boolean reduceConnections; private ProcessorConfiguration configuration; private int fifoSize; public void setOptions(Map<String, Object> options) { if (options.containsKey(TTA_CONNECTION_REDUCTION)) { reduceConnections = (Boolean) options.get(TTA_CONNECTION_REDUCTION); } else { reduceConnections = TTA_CONNECTION_REDUCTION_DEFAULT; } String confName; if (options.containsKey(TTA_PROCESSORS_CONFIGURATION)) { confName = (String) options.get(TTA_PROCESSORS_CONFIGURATION); } else { confName = TTA_DEFAULT_PROCESSORS_CONFIGURATION; } configuration = ProcessorConfiguration.getByName(confName); if (options.containsKey(FIFO_SIZE)) { fifoSize = (Integer) options.get(FIFO_SIZE); } else { fifoSize = DEFAULT_FIFO_SIZE; } } /** * Add a simple signal to the design. The signal is the translation of * native connection. External ports and functional units are automatically * added to the design and their processor. * * @param connection * the native connection which represents the signal */ private void addSignal(Connection connection) { Vertex source = componentMap.get(connection.getSource()); Vertex target = componentMap.get(connection.getTarget()); Port sourcePort = null; Port targetPort = null; int size; if (source == null) { // The signal comes from an external port Port port = factory.createPort((net.sf.orcc.df.Port) connection .getSource()); design.addInput(port); source = port; } else { if (source instanceof Processor) { Processor processor = (Processor) source; FunctionUnit fu = factory.createOutputSignalUnit(processor, connection.getSourcePort().getName()); processor.getFunctionUnits().add(fu); sourcePort = fu; } else { Component component = (Component) source; sourcePort = factory.createPort(connection.getSourcePort()); component.addOutput(sourcePort); } } if (target == null) { // The signal targets an external port Port port = factory.createPort((net.sf.orcc.df.Port) connection .getTarget()); design.addOutput(port); size = port.getSize(); target = port; } else { if (target instanceof Processor) { throw new OrccRuntimeException("Unsupported input signal."); } else { Component component = (Component) target; targetPort = factory.createPort(connection.getTargetPort()); component.addInput(targetPort); size = targetPort.getSize(); } } Signal signal = factory.createSignal("" + signalId++, size, source, target, sourcePort, targetPort); design.add(signal); } /** * Build a design from a network of actors, a predefined configuration of * the processors and finally the mapping of the actors on a set of * processors. The processors and their interconnections are instantiated * during the visit of a given process network. * * @param network * The dataflow network of the current description to compile * @param configuration * A predefined configuration of the processors * @param mapping * The mapping of the actors contained by the network on a set of * processors * @return A new design */ public Design build(Network network, Mapping mapping) { this.componentMap = new HashMap<Vertex, Component>(); this.design = factory.createDesign(); bufferId = 0; signalId = 0; // Map all unmapped component to its own processor for (Vertex unmapped : new ArrayList<Vertex>(mapping.getUnmapped())) { mapping.map("processor_" + unmapped.getLabel(), unmapped); } // Build processors for (String name : mapping.getComponents()) { design.add(factory.createProcessor(name, configuration, 0)); } // Map Actors for (Vertex vertex : network.getChildren()) { Instance instance = vertex.getAdapter(Instance.class); Actor actor = vertex.getAdapter(Actor.class); if (actor.isNative()) { // A native actor describes a VHDL component from the library. Component component = factory .createComponent(vertex.getLabel()); // The parameter of this actor is used as generic. if (instance != null) { for (Argument arg : instance.getArguments()) { component.setAttribute(arg.getVariable().getName(), arg.getValue()); } } else { for (Var param : actor.getParameters()) { component.setAttribute(param.getName(), param.getInitialValue()); } } design.add(component); componentMap.put(vertex, component); } else { Processor processor = design.getProcessor(mapping .getComponent(vertex)); processor.getMappedActors().add(vertex); componentMap.put(vertex, processor); } } // Map FIFOs for (Connection connection : network.getConnections()) { if (isNative(connection)) { // Native connection are hardware signals addSignal(connection); } else { // FIFO connection are mapped to a memory mapToBuffer(connection); } } new ArchitectureMemoryEstimator(fifoSize).doSwitch(design); for (Processor processor : design.getProcessors()) { if (ArchitectureUtil.needOrccFu(processor.getMappedActors())) { processor.getFunctionUnits().add( factory.createOrccFU(processor)); } if (ArchitectureUtil.needToPrint(processor.getMappedActors())) { processor.getFunctionUnits().add(factory.createIoFU(processor)); } } OrccLogger.traceln("Processor configuration : " + configuration.getName()); OrccLogger.traceln("Design configuration : " + design.getProcessors().size() + " processors - " + design.getSharedMemories().size() + " shared RAMs"); return design; } private boolean isNative(Connection connection) { net.sf.orcc.df.Port source = connection.getSourcePort(); net.sf.orcc.df.Port target = connection.getTargetPort(); return (source != null && source.isNative()) || (target != null && target.isNative()); } /** * Map a connection on a circular buffer. If the actors source and target of * the connection are mapped on different processors, then the connection is * mapped to their interconnected memory and this memory is created in case * it doesn't yet exist. If both actors are mapped to the same processor, * then the connection is mapped to its internal RAM. * * @param connection * the connection to map on a buffer */ private void mapToBuffer(Connection connection) { Processor source = (Processor) componentMap.get(connection.getSource()); Processor target = (Processor) componentMap.get(connection.getTarget()); if (source == null || target == null) { // One of them is a network port. OrccLogger.warnln("External FIFO port: The given application " + "cannot be synthesised."); Processor proc = source == null ? target : source; Memory ram = factory.createMemory("smem_" + bufferId); proc.connect(ram); return; } Memory ram; if (reduceConnections && source.getNeighbors().contains(target)) { ram = source.getMemorySharedWith(target); } else if (source == target) { // If both source and target are mapped to the same processor, then // the connection will be mapped to a local RAM if (reduceConnections && !source.getLocalRAMs().isEmpty()) { // An existing local RAM ram = source.getLocalRAMs().get(0); } else { // Or a newly created one ram = factory.createMemory("lmem_" + bufferId); source.connect(ram); source.getLocalRAMs().add(ram); ram.setAttribute("id", bufferId++); } } else { // Creation of a new shared memory connected to both processors. ram = factory.createMemory("smem_" + bufferId); FunctionUnit sourceLSU = source.connect(ram); FunctionUnit targetLSU = target.connect(ram); ram.setSource(source); ram.setTarget(target); ram.setSourcePort(sourceLSU); ram.setTargetPort(targetLSU); design.add(ram); ram.setAttribute("id", bufferId++); } ram.getMappedConnections().add(connection); } }