| /** |
| ******************************************************************************** |
| * Copyright (c) 2015-2020 Robert Bosch GmbH and others. |
| * |
| * This program and the accompanying materials are made |
| * available under the terms of the Eclipse Public License 2.0 |
| * which is available at https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Robert Bosch GmbH - initial API and implementation |
| ******************************************************************************** |
| */ |
| |
| package org.eclipse.app4mc.amalthea.model.util; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| |
| import org.eclipse.app4mc.amalthea.model.Amalthea; |
| import org.eclipse.app4mc.amalthea.model.AmaltheaServices; |
| import org.eclipse.app4mc.amalthea.model.Cache; |
| import org.eclipse.app4mc.amalthea.model.CacheDefinition; |
| import org.eclipse.app4mc.amalthea.model.ConnectionHandler; |
| import org.eclipse.app4mc.amalthea.model.ConnectionHandlerDefinition; |
| import org.eclipse.app4mc.amalthea.model.DataRate; |
| import org.eclipse.app4mc.amalthea.model.Frequency; |
| import org.eclipse.app4mc.amalthea.model.HwAccessElement; |
| import org.eclipse.app4mc.amalthea.model.HwAccessPath; |
| import org.eclipse.app4mc.amalthea.model.HwConnection; |
| import org.eclipse.app4mc.amalthea.model.HwDestination; |
| import org.eclipse.app4mc.amalthea.model.HwModule; |
| import org.eclipse.app4mc.amalthea.model.HwPathElement; |
| import org.eclipse.app4mc.amalthea.model.HwStructure; |
| import org.eclipse.app4mc.amalthea.model.IDiscreteValueDeviation; |
| import org.eclipse.app4mc.amalthea.model.Memory; |
| import org.eclipse.app4mc.amalthea.model.ProcessingUnit; |
| import org.eclipse.app4mc.amalthea.model.ProcessingUnitDefinition; |
| import org.eclipse.app4mc.amalthea.model.Time; |
| import org.eclipse.app4mc.amalthea.model.util.RuntimeUtil.AccessDirection; |
| import org.eclipse.app4mc.amalthea.model.util.RuntimeUtil.TimeType; |
| |
| public class HardwareUtil { |
| |
| // Suppress default constructor |
| private HardwareUtil() { |
| throw new IllegalStateException("Utility class"); |
| } |
| |
| public static <T extends HwModule> List<T> getModulesFromHwModel(Class<T> targetClass, Amalthea model) { |
| List<T> results = new ArrayList<>(); |
| for (HwStructure structure : model.getHwModel().getStructures()) { |
| collectModulesFromHWStructure(structure, targetClass, results); |
| } |
| return results; |
| } |
| |
| public static <T extends HwModule> List<T> getModulesFromHWStructure(Class<T> targetClass, HwStructure structure) { |
| List<T> results = new ArrayList<>(); |
| collectModulesFromHWStructure(structure, targetClass, results); |
| |
| return results; |
| } |
| |
| private static <T extends HwModule> void collectModulesFromHWStructure(HwStructure structure, Class<T> targetClass, List<T> results) { |
| // check all modules |
| for (HwModule module : structure.getModules()) { |
| if (targetClass.isInstance(module)) { |
| results.add(targetClass.cast(module)); |
| } |
| |
| // special handling of processing unit caches |
| if (targetClass.isAssignableFrom(Cache.class) && module instanceof ProcessingUnit) { |
| for (Cache containedCache : ((ProcessingUnit) module).getCaches()) { |
| if (targetClass.isInstance(containedCache)) { |
| results.add(targetClass.cast(containedCache)); |
| } |
| } |
| } |
| } |
| |
| // call method recursive to also get modules of included structures |
| for (HwStructure hwStruct : structure.getStructures()) { |
| collectModulesFromHWStructure(hwStruct, targetClass, results); |
| } |
| } |
| |
| public static List<ProcessingUnit> getAllProcessingUnitsForProcessingUnitDefinition(Amalthea model, ProcessingUnitDefinition puDef) { |
| if (puDef == null) { // null is the key for default values! |
| return new ArrayList<>(); |
| } |
| |
| List<ProcessingUnit> result = new ArrayList<>(); |
| for (ProcessingUnit pu : getModulesFromHwModel(ProcessingUnit.class, model)) { |
| if (puDef.equals(pu.getDefinition())) { |
| result.add(pu); |
| } |
| } |
| |
| return result; |
| } |
| |
| public static Map<Memory, Long> getMemoryAccessLatenciesCycles(Amalthea model, TimeType timeType) { |
| HashMap<Memory, Long> result = new HashMap<>(); |
| List<Memory> mems = getModulesFromHwModel(Memory.class, model); |
| for (Memory mem : mems) { |
| result.put(mem, calculateLatency(mem.getDefinition().getAccessLatency(), timeType)); |
| } |
| return result; |
| } |
| |
| public static Map<Memory, Time> getMemoryAccessLatencyTime(Amalthea model, TimeType timeType) { |
| HashMap<Memory, Time> result = new HashMap<>(); |
| Map<Memory, Long> memoryMap = getMemoryAccessLatenciesCycles(model, timeType); |
| |
| for (Entry<Memory, Long> entry : memoryMap.entrySet()) { |
| final Memory memory = entry.getKey(); |
| final double cycles = entry.getValue().floatValue(); |
| final Frequency defaultFrequency = memory.getFrequencyDomain().getDefaultValue(); |
| Time time = RuntimeUtil.getExecutionTimeForCycles(cycles, defaultFrequency); |
| result.put(memory, time); |
| } |
| |
| return result; |
| } |
| |
| public static List<HwAccessElement> getAccessElementsToDestination(HwDestination dest, Amalthea model) { |
| List<HwAccessElement> result = new ArrayList<>(); |
| List<ProcessingUnit> pus = getModulesFromHwModel(ProcessingUnit.class, model); |
| for (ProcessingUnit pu : pus) { |
| for (HwAccessElement element : pu.getAccessElements()) { |
| if (element.getDestination().equals(dest)) { |
| result.add(element); |
| } |
| } |
| } |
| return result; |
| } |
| |
| public static Map<ProcessingUnit, HashMap<HwDestination, Time>> getAccessTimes(Amalthea model, TimeType timeType, |
| AccessDirection direction) { |
| Map<ProcessingUnit, HashMap<HwDestination, Time>> coreMemoryLatency = new HashMap<>(); |
| List<ProcessingUnit> puList = getModulesFromHwModel(ProcessingUnit.class, model); |
| for (ProcessingUnit pu : puList) { |
| HashMap<HwDestination, Time> memoryAccessMap = new HashMap<>(); |
| for (HwAccessElement accessElement : pu.getAccessElements()) { |
| HwDestination destination = accessElement.getDestination(); |
| Time latency = null; |
| if (accessElement.getAccessPath() != null) { |
| latency = calculateHwAccessPathTime(accessElement, timeType, direction); |
| } else { |
| latency = calculateLatencyPathTime(accessElement, timeType, direction); |
| } |
| |
| Time previousLatency = memoryAccessMap.get(destination); |
| if (previousLatency == null || (latency != null && AmaltheaServices.compareTimes(previousLatency, latency) < 0)) { |
| memoryAccessMap.put(destination, latency); |
| } |
| } |
| coreMemoryLatency.put(pu, memoryAccessMap); |
| } |
| return coreMemoryLatency; |
| } |
| |
| public static Time calculateLatencyPathTime(HwAccessElement accessElement, TimeType timeType, |
| AccessDirection direction) { |
| IDiscreteValueDeviation latency = null; |
| switch (direction) { |
| case READ: |
| if (accessElement.getReadLatency() != null) { |
| latency = accessElement.getReadLatency(); |
| } |
| break; |
| case WRITE: |
| if (accessElement.getWriteLatency() != null) { |
| latency = accessElement.getWriteLatency(); |
| } |
| break; |
| default: |
| break; |
| } |
| |
| return RuntimeUtil.getExecutionTimeForCycles(calculateLatency(latency, timeType), |
| accessElement.getSource().getFrequencyDomain().getDefaultValue()); |
| } |
| |
| public static Time calculateHwAccessPathTime(HwAccessElement accessElement, TimeType timeType, AccessDirection direction) { |
| if (accessElement == null || accessElement.getAccessPath() == null) return null; |
| |
| Time result = FactoryUtil.createTime(); |
| Frequency frequency = null; |
| IDiscreteValueDeviation latency = null; |
| |
| for (HwPathElement element : accessElement.getAccessPath().getPathElements()) { |
| if (element instanceof ConnectionHandler) { |
| latency = getLatency((ConnectionHandler) element, direction); |
| frequency = getFrequencyOfModule((ConnectionHandler) element); |
| } else if (element instanceof Cache) { |
| latency = getLatency((Cache) element); |
| frequency = getFrequencyOfModule((Cache) element); |
| } else if (element instanceof HwConnection) { |
| latency = getLatency((HwConnection) element, direction); |
| // Assumption is that if the frequencyOfComponent is null the HwConnection is |
| // the first element in the path, in this case the frequency of the source |
| // (ProcessingUnit) |
| // is the driver for the frequency. In any other case the element in front of |
| // the connection is the driver (The HwPath is an ordered list of path elements) |
| if (frequency == null) { |
| frequency = getFrequencyOfModule(accessElement.getSource()); |
| } |
| } |
| // It is not possible to specify a Read or Write Latencies for ProcessingUnits! |
| // In the case this causes some issues in future the interface HwPathElement |
| // could be removed from ProcessingUnit |
| else if (element instanceof ProcessingUnit) { |
| latency = null; |
| frequency = getFrequencyOfModule((ProcessingUnit) element); |
| } |
| |
| Long tmpLatency = calculateLatency(latency, timeType); |
| Time executionTimeForCycles = RuntimeUtil.getExecutionTimeForCycles(tmpLatency, frequency); |
| result = result.add(executionTimeForCycles); |
| } |
| |
| return result.adjustUnit(); |
| } |
| |
| private static IDiscreteValueDeviation getLatency(ConnectionHandler handler, AccessDirection direction) { |
| final ConnectionHandlerDefinition definition = handler.getDefinition(); |
| if (definition == null) return null; |
| |
| return (direction.equals(AccessDirection.READ)) ? definition.getReadLatency() : definition.getWriteLatency(); |
| } |
| |
| private static IDiscreteValueDeviation getLatency(Cache cache) { |
| final CacheDefinition definition = cache.getDefinition(); |
| if (definition == null) return null; |
| |
| return definition.getAccessLatency(); |
| } |
| |
| private static IDiscreteValueDeviation getLatency(HwConnection connection, AccessDirection direction) { |
| return (direction.equals(AccessDirection.READ)) ? connection.getReadLatency() : connection.getWriteLatency(); |
| } |
| |
| public static Long calculateLatency(IDiscreteValueDeviation latency, TimeType timeType) { |
| if (latency == null) return 0L; |
| |
| switch (timeType) { |
| case BCET: |
| return latency.getLowerBound(); |
| case ACET: |
| return (latency.getAverage() != null) ? latency.getAverage().longValue() : null; |
| case WCET: |
| return latency.getUpperBound(); |
| } |
| |
| return (latency.getAverage() != null) ? latency.getAverage().longValue() : null; |
| } |
| |
| /** |
| * Computes the minimum data rate of an access path |
| */ |
| public static DataRate getMinDataRateOfHwAccessPath(HwAccessPath path) { |
| if (path == null) return null; |
| |
| DataRate minimum = null; |
| |
| for (HwPathElement element : path.getPathElements()) { |
| // try to get data rate |
| DataRate dataRate = null; |
| if (element instanceof ConnectionHandler) { |
| dataRate = ((ConnectionHandler) element).getDefinition().getDataRate(); |
| } else if (element instanceof HwConnection) { |
| dataRate = ((HwConnection) element).getDataRate(); |
| } |
| |
| if (dataRate == null) |
| continue; |
| |
| // update minimum |
| if (minimum == null || AmaltheaServices.compareDataRates(dataRate, minimum) < 0) { |
| minimum = dataRate; |
| } |
| } |
| |
| return minimum; |
| } |
| |
| /** |
| * Returns the frequency of a specific module |
| */ |
| public static Frequency getFrequencyOfModule(HwModule module) { |
| return module.getFrequencyDomain().getDefaultValue(); |
| } |
| |
| /** |
| * Returns the frequency of a specific module in Hertz |
| */ |
| public static long getFrequencyOfModuleInHz(HwModule module) { |
| final Frequency frequencyOfModule = getFrequencyOfModule(module); |
| if (frequencyOfModule == null) return 0L; |
| |
| return AmaltheaServices.convertToHertz(frequencyOfModule).longValue() ; |
| } |
| } |