blob: f9408150be6e63a419beb99e05db0579e140f31a [file] [log] [blame]
/**
********************************************************************************
* 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() ;
}
}