Added Event Model Implementation Signed-off-by: Lukas Krawczyk <eclipse@krawczyk.at>
diff --git a/plugins/org.eclipse.app4mc.amalthea.model/src/org/eclipse/app4mc/amalthea/model/util/stimuli/EMPeriodic.java b/plugins/org.eclipse.app4mc.amalthea.model/src/org/eclipse/app4mc/amalthea/model/util/stimuli/EMPeriodic.java new file mode 100644 index 0000000..0639f56 --- /dev/null +++ b/plugins/org.eclipse.app4mc.amalthea.model/src/org/eclipse/app4mc/amalthea/model/util/stimuli/EMPeriodic.java
@@ -0,0 +1,185 @@ +/** + ******************************************************************************** + * Copyright (c) 2022 Dortmund University of Applied Sciences and Arts 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: + * Dortmund University of Applied Sciences and Arts - initial API and implementation + ******************************************************************************** + */ + +package org.eclipse.app4mc.amalthea.model.util.stimuli; + +import java.math.BigInteger; + +import org.eclipse.app4mc.amalthea.model.ModeLabel; +import org.eclipse.app4mc.amalthea.model.PeriodicStimulus; +import org.eclipse.app4mc.amalthea.model.Process; +import org.eclipse.app4mc.amalthea.model.Time; +import org.eclipse.app4mc.amalthea.model.util.FactoryUtil; +import org.eclipse.app4mc.amalthea.model.util.RuntimeUtil; +import org.eclipse.app4mc.amalthea.model.util.RuntimeUtil.TimeType; +import org.eclipse.emf.common.util.EMap; + +/** + * Event Model functions for APP4MC's {@link PeriodicStimulus}. + * <p> + * For a full documentation of the underlying functions, please refer to the + * <a href="https://doi.org/10.1016/j.sysarc.2021.102343">related + * publication</a>, Section 3.1. + */ +public class EMPeriodic implements IEventModel { + /** + * The actual period of this trigger pattern + */ + private Time tOuterPeriod; + /** + * Minimum distance between two subsequent activation events + */ + private Time tMinDistance; + /** + * Maximum jitter by which an activation event can be delayed + */ + private Time tJitter; + + public EMPeriodic() { + // Empty on purpose + } + + @Override + public long etaPlus(final Time dt) { + // Return 0 on dt=0 + if (dt.getValue().compareTo(BigInteger.ZERO) == 0) { + return 0; + } + + // LHS: ceil(dt+J/T) + final Time num = dt.add(this.tJitter); + final double eta_plus_p = Math.ceil(num.divide(this.tOuterPeriod)); + + // RHS: ceil(dt/d) + final double eta_plus; + if (this.tMinDistance.getValue().compareTo(BigInteger.ZERO) != 0) { + final double eta_plus_d = Math.ceil(dt.divide(this.tMinDistance)); + eta_plus = Math.min(eta_plus_p, eta_plus_d); + } else { + eta_plus = eta_plus_p; + } + return (long) eta_plus; + } + + @Override + public long etaMinus(final Time dt) { + // Return 0 on dt=0 + if (dt.getValue().compareTo(BigInteger.ZERO) == 0) { + return 0; + } + + // LHS: floor(dt-J/T) + final Time num = dt.subtract(this.tJitter); + final double eta_min_p = Math.floor(num.divide(this.tOuterPeriod)); + + // RHS: floor(dt/d) + final double eta_min; + if (this.tMinDistance.getValue().compareTo(BigInteger.ZERO) != 0) { + final double eta_min_d = Math.floor(dt.divide(this.tMinDistance)); + eta_min = Math.max(eta_min_p, eta_min_d); + } else { + eta_min = eta_min_p; + } + + return (long) Math.max(0, eta_min); + } + + @Override + public Time deltaPlus(final long n) { + // Function is only valid for n >= 2, return null on invalid parameter + if (n < 2) { + return null; + } + + // Compare recurrence and minDistance, use the *highest* of both for + // further calculation + final Time tMinDistanceCalc = EventModelFactory.max(this.tOuterPeriod, this.tMinDistance); + + // Multiply this distance with the number of events (minus one) + final Time tDistance = tMinDistanceCalc.multiply(n - 1); + + return tDistance.add(this.tJitter); + + } + + @Override + public Time deltaMinus(final long n) { + // Function is only valid for n >= 2, return 0 on invalid parameter + if (n < 2) { + return FactoryUtil.createTime("0ms"); + } + + // Compare recurrence and minDistance, use the *highest* of both for + // further calculation + final Time tMinDistanceCalc = EventModelFactory.max(this.tOuterPeriod, this.tMinDistance); + + // Multiply this distance with the number of events (minus one) + final Time tDistance = tMinDistanceCalc.multiply(n - 1); + + /* + * Subtract jitter if and only if it does not decrease the distance between two + * activation events to less than tMinDistance. Cap it otherwise to the max. + * permitted value that does not violate tMinDistance. + */ + // Check if tRecurrence > tMinDistance (Prerequisite, otherwise jitter + // is capped at 0) + if (this.tOuterPeriod.compareTo(this.tMinDistance) > 0) { + final Time tMaxJitter; + final Time tDiffRecMinDistance = this.tOuterPeriod.subtract(this.tMinDistance); + // Check if Jitter exceeds (tRecurrence - tMinDistane) + if (this.tJitter.compareTo(tDiffRecMinDistance) > 0) { + // Yes, so max. Jitter must be capped at tRecurrence - + // tMinDistane + tMaxJitter = tDiffRecMinDistance; + } else { + // No, so max. Jitter can be taken as specified + tMaxJitter = this.tJitter; + } + return tDistance.subtract(tMaxJitter); + } + + return tDistance; + } + + @Override + public double getUtilization(Process process, TimeType tt, EMap<ModeLabel, String> modes) { + final Time time = RuntimeUtil.getExecutionTimeForProcess(process, modes, tt); + return time.divide(this.tOuterPeriod); + } + + public Time getRecurrence() { + return this.tOuterPeriod; + } + + public void setRecurrence(Time recurrence) { + this.tOuterPeriod = recurrence; + } + + public Time getMinDistance() { + return this.tMinDistance; + } + + public void setMinDistance(Time minDistance) { + this.tMinDistance = minDistance; + } + + public Time getJitter() { + return this.tJitter; + } + + public void setJitter(Time jitter) { + this.tJitter = jitter; + } +}
diff --git a/plugins/org.eclipse.app4mc.amalthea.model/src/org/eclipse/app4mc/amalthea/model/util/stimuli/EMPeriodicBurst.java b/plugins/org.eclipse.app4mc.amalthea.model/src/org/eclipse/app4mc/amalthea/model/util/stimuli/EMPeriodicBurst.java new file mode 100644 index 0000000..f334f80 --- /dev/null +++ b/plugins/org.eclipse.app4mc.amalthea.model/src/org/eclipse/app4mc/amalthea/model/util/stimuli/EMPeriodicBurst.java
@@ -0,0 +1,241 @@ +/** + ******************************************************************************** + * Copyright (c) 2022 Dortmund University of Applied Sciences and Arts 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: + * Dortmund University of Applied Sciences and Arts - initial API and implementation + ******************************************************************************** + */ + +package org.eclipse.app4mc.amalthea.model.util.stimuli; + +import org.eclipse.app4mc.amalthea.model.ModeLabel; +import org.eclipse.app4mc.amalthea.model.PeriodicBurstStimulus; +import org.eclipse.app4mc.amalthea.model.Process; +import org.eclipse.app4mc.amalthea.model.Time; +import org.eclipse.app4mc.amalthea.model.util.FactoryUtil; +import org.eclipse.app4mc.amalthea.model.util.RuntimeUtil; +import org.eclipse.app4mc.amalthea.model.util.RuntimeUtil.TimeType; +import org.eclipse.emf.common.util.EMap; + +/** + * Event Model functions for APP4MC's {@link PeriodicBurstStimulus}. + * <p> + * For a full documentation of the underlying functions, please refer to the + * <a href="https://doi.org/10.1016/j.sysarc.2021.102343">related + * publication</a>, Section 3.3. + */ +public class EMPeriodicBurst implements IEventModel { + /** + * Constant representing a Time object corresponding to 0ms. + */ + private static final Time ZEROMS = FactoryUtil.createTime("0ms"); + /** + * The actual period of this trigger pattern + */ + private Time tOuterPeriod; + /** + * Duration of a single burst, i.e., the length of the time window in which + * activation events are released + */ + private Time tBurstLength; + /** + * Number of activation events triggered per burst + */ + private long iOccurrenceCount; + /** + * Minimum distance between two subsequent activation events + */ + private Time tMinDistance; + /** + * Internal offset by how much a time window in which activation events are + * released can be delayed + */ + private Time tDeltaBurst; + + public EMPeriodicBurst() { + // Empty on purpose + } + + @Override + public long etaPlus(final Time dt) { + // Return 0 on dt=0 + if (EventModelFactory.isNullOrZero(dt)) { + // For dt = 0, the upper and lower bound functions return 0 + return 0; + } + + // Check for first case, tMin is unconstrained + if (EventModelFactory.isNullOrZero(this.tMinDistance)) { + final double dEtaScale = dt.add(this.tBurstLength).divide(this.tOuterPeriod); + return (long) (this.iOccurrenceCount * Math.ceil(dEtaScale)); + } + + // Second case, tMin is constrained + // EtaOuter + final double fraction = Math.floor(dt.add(this.tDeltaBurst).divide(this.tOuterPeriod)); + final long iEtaOuter = (long) (this.iOccurrenceCount * fraction); + + // EtaInner + // Check whether dt < TO - DeltaBurst + final long iEtaInner; + + if (dt.compareTo(this.tOuterPeriod.subtract(this.tDeltaBurst)) < 0) { + // Return the minimum among b and ceil(dt/dmin) + iEtaInner = (long) Math.min(Math.ceil(dt.divide(this.tMinDistance)), this.iOccurrenceCount); + } else { + /* + * Calculate etaInner, i.e. the number of releases per "remainder" (dt mod + * T^Outer) + */ + // dt + db - T^Outer * floor((dt + db) /T^Outer) + // Calculate remainder (dt') + final Time rhs = this.tOuterPeriod.multiply(Math.floor(dt.add(this.tDeltaBurst).divide(this.tOuterPeriod))); + final Time tRemainder = dt.add(this.tDeltaBurst).subtract(rhs); + iEtaInner = (long) Math.min(Math.ceil(tRemainder.divide(this.tMinDistance)), this.iOccurrenceCount); + } + + return iEtaOuter + iEtaInner; + } + + @Override + public long etaMinus(final Time dt) { + // Return 0 on dt=0 + if (EventModelFactory.isNullOrZero(dt)) { + // For dt = 0, the upper and lower bound functions return 0 + return 0; + } + + // Check for first case, tMin is unconstrained + if (EventModelFactory.isNullOrZero(this.tMinDistance)) { + final double dEtaScale = dt.subtract(this.tBurstLength).divide(this.tOuterPeriod); + return (long) Math.max(0, this.iOccurrenceCount * Math.floor(dEtaScale)); + } + + // Calculate dt' + // dt - (R + BL - 2(b-1)tMin) + 1 + final Time dtOffsetRaw = dt + .subtract(this.tOuterPeriod.add(this.tBurstLength) + .subtract(this.tMinDistance.multiply(this.iOccurrenceCount - 1).multiply(2))) + .add(this.tMinDistance); + // Max(dtOffsetRaw, 0) + final Time dtOffset = (dtOffsetRaw.compareTo(ZEROMS) > 0) ? dtOffsetRaw : ZEROMS; + + // etaOuter + final double fraction = Math.floor(dtOffset.divide(this.tOuterPeriod)); + final long iEtaOuter = (long) (this.iOccurrenceCount * fraction); + + /* + * Calculate etaInner, i.e. the number of releases per "remainder" (dt mod + * T^Outer) + */ + if (EventModelFactory.isNullOrZero(this.tMinDistance)) { + return iEtaOuter; + } + // dt - T^Outer * floor(dt/T^Outer) + final Time rhs = this.tOuterPeriod.multiply(Math.floor(dtOffset.divide(this.tOuterPeriod))); + final Time tRemainder = dtOffset.subtract(rhs); + final long iEtaInner = (long) Math.min(this.iOccurrenceCount, Math.ceil(tRemainder.divide(this.tMinDistance))); + + return iEtaOuter + iEtaInner; + } + + @Override + public Time deltaPlus(final long n) { + // Function is only valid for n >= 2, return null on invalid parameter + if (n < 2) { + return null; + } + + // Integer division on purpose in order to round the result down + final long ceiledFraction = (n - 1) / this.iOccurrenceCount; + // Distance between full periods + final Time tFull = this.tOuterPeriod.multiply(ceiledFraction); + + // Distance between remaining events + final Time tPartial; + final Time tSlack = this.tOuterPeriod.subtract(this.tMinDistance.multiply(this.iOccurrenceCount - 1)); + if (tSlack.compareTo(this.tMinDistance) > 0) { + tPartial = this.tOuterPeriod.subtract(this.tMinDistance.multiply(this.iOccurrenceCount - (n - 1))); + } else { + tPartial = this.tMinDistance.multiply(n - 1); + } + return tFull.add(tPartial); + } + + @Override + public Time deltaMinus(final long n) { + // Function is only valid for n >= 2, return null on invalid parameter + if (n < 2) { + return null; + } + + // Integer division on purpose in order to round the result down + final long ceiledFraction = (n - 1) / this.iOccurrenceCount; + + // Distance between full periods + final Time tFull = this.tOuterPeriod.multiply(ceiledFraction); + // Distance between remaining events + final long remainder = n - 1 - ceiledFraction * this.iOccurrenceCount; + final Time tPartial = this.tMinDistance.multiply(remainder); + + return tFull.add(tPartial); + } + + @Override + public double getUtilization(Process process, TimeType tt, EMap<ModeLabel, String> modes) { + // Note that bursts are considered to be strictly periodic in APP4MC. + // Accordingly, the outer period is assumed to be constant (without any drift) + // and the maximum number of activation events will be released with each burst. + // Therefore, the load is only influenced by a process's execution time, thus + // making it unnecessary to handle different cases of periods. + final Time executionTime = RuntimeUtil.getExecutionTimeForProcess(process, modes, tt); + return executionTime.multiply(getOccurrenceCount()).divide(getOuterPeriod()); + } + + public Time getOuterPeriod() { + return tOuterPeriod; + } + + public void setOuterPeriod(Time tOuterPeriod) { + this.tOuterPeriod = tOuterPeriod; + } + + public Time getBurstLength() { + return tBurstLength; + } + + public void setBurstLength(Time tBurstLength) { + this.tBurstLength = tBurstLength; + } + + public long getOccurrenceCount() { + return iOccurrenceCount; + } + + public void setOccurrenceCount(long iOccurrenceCount) { + this.iOccurrenceCount = iOccurrenceCount; + } + + public Time getMinDistance() { + return tMinDistance; + } + + public void setMinDistance(Time tMinDistance) { + this.tMinDistance = tMinDistance; + } + + public Time getDeltaBurst() { + return tDeltaBurst; + } + + public void setDeltaBurst(Time tDeltaBurst) { + this.tDeltaBurst = tDeltaBurst; + } +}
diff --git a/plugins/org.eclipse.app4mc.amalthea.model/src/org/eclipse/app4mc/amalthea/model/util/stimuli/EMPeriodicRelative.java b/plugins/org.eclipse.app4mc.amalthea.model/src/org/eclipse/app4mc/amalthea/model/util/stimuli/EMPeriodicRelative.java new file mode 100644 index 0000000..598f3cc --- /dev/null +++ b/plugins/org.eclipse.app4mc.amalthea.model/src/org/eclipse/app4mc/amalthea/model/util/stimuli/EMPeriodicRelative.java
@@ -0,0 +1,121 @@ +/** + ******************************************************************************** + * Copyright (c) 2022 Dortmund University of Applied Sciences and Arts 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: + * Dortmund University of Applied Sciences and Arts - initial API and implementation + ******************************************************************************** + */ + +package org.eclipse.app4mc.amalthea.model.util.stimuli; + +import java.math.BigInteger; + +import org.eclipse.app4mc.amalthea.model.ModeLabel; +import org.eclipse.app4mc.amalthea.model.Process; +import org.eclipse.app4mc.amalthea.model.RelativePeriodicStimulus; +import org.eclipse.app4mc.amalthea.model.Time; +import org.eclipse.app4mc.amalthea.model.util.RuntimeUtil; +import org.eclipse.app4mc.amalthea.model.util.RuntimeUtil.TimeType; +import org.eclipse.emf.common.util.EMap; + +/** + * Event Model functions for APP4MC's {@link RelativePeriodicStimulus}. + * <p> + * For a full documentation of the underlying functions, please refer to the + * <a href="https://doi.org/10.1016/j.sysarc.2021.102343">related + * publication</a>, Section 3.2. + */ +public class EMPeriodicRelative implements IEventModel { + /** + * Upper bound (i.e., maximum) distance between two subsequent activation + * events. + */ + private Time upperBoundDistance; + /** + * Lower bound (i.e., minimum) distance between two subsequent activation + * events. + */ + private Time lowerBoundDistance; + + public EMPeriodicRelative() { + // Empty on purpose + } + + @Override + public long etaPlus(final Time dt) { + if (dt.getValue().compareTo(BigInteger.ZERO) == 0) { + return 0; + } + + return (long) Math.ceil(dt.divide(this.lowerBoundDistance)); + } + + @Override + public long etaMinus(final Time dt) { + if (dt.getValue().compareTo(BigInteger.ZERO) == 0) { + return 0; + } + + return (long) Math.floor(dt.divide(this.upperBoundDistance)); + } + + @Override + public Time deltaPlus(final long n) { + // Function is only valid for n >= 2, return null on invalid parameter + if (n < 2) { + return null; + } + + return this.upperBoundDistance.multiply(n - 1); + } + + @Override + public Time deltaMinus(final long n) { + // Function is only valid for n >= 2, return null on invalid parameter + if (n < 2) { + return null; + } + + return this.lowerBoundDistance.multiply(n - 1); + } + + @Override + public double getUtilization(Process process, TimeType tt, EMap<ModeLabel, String> modes) { + final Time time = RuntimeUtil.getExecutionTimeForProcess(process, modes, tt); + if (tt.equals(TimeType.BCET)) { + // Minimum load occurs when activation events occur with maximum distance + return time.divide(this.upperBoundDistance); + } else if (tt.equals(TimeType.WCET)) { + // Maximum load occurs when activation events occur with minimum distance + return time.divide(this.lowerBoundDistance); + } else if (tt.equals(TimeType.ACET)) { + final Time averageDistance = this.lowerBoundDistance.add(this.upperBoundDistance).multiply(0.5); + return time.divide(averageDistance); + } + // Unsupported configuration + return -1; + } + + public Time getUpperBoundDistance() { + return upperBoundDistance; + } + + public void setUpperBoundDistance(Time upperBoundDistance) { + this.upperBoundDistance = upperBoundDistance; + } + + public Time getLowerBoundDistance() { + return lowerBoundDistance; + } + + public void setLowerBoundDistance(Time lowerBoundDistance) { + this.lowerBoundDistance = lowerBoundDistance; + } +}
diff --git a/plugins/org.eclipse.app4mc.amalthea.model/src/org/eclipse/app4mc/amalthea/model/util/stimuli/EMPeriodicSynthetic.java b/plugins/org.eclipse.app4mc.amalthea.model/src/org/eclipse/app4mc/amalthea/model/util/stimuli/EMPeriodicSynthetic.java new file mode 100644 index 0000000..72dcc3c --- /dev/null +++ b/plugins/org.eclipse.app4mc.amalthea.model/src/org/eclipse/app4mc/amalthea/model/util/stimuli/EMPeriodicSynthetic.java
@@ -0,0 +1,273 @@ +/** + ******************************************************************************** + * Copyright (c) 2022 Dortmund University of Applied Sciences and Arts 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: + * Dortmund University of Applied Sciences and Arts - initial API and implementation + ******************************************************************************** + */ + +package org.eclipse.app4mc.amalthea.model.util.stimuli; + +import java.math.BigInteger; +import java.util.List; + +import org.eclipse.app4mc.amalthea.model.ModeLabel; +import org.eclipse.app4mc.amalthea.model.PeriodicSyntheticStimulus; +import org.eclipse.app4mc.amalthea.model.Process; +import org.eclipse.app4mc.amalthea.model.Time; +import org.eclipse.app4mc.amalthea.model.util.RuntimeUtil; +import org.eclipse.app4mc.amalthea.model.util.RuntimeUtil.TimeType; +import org.eclipse.emf.common.util.EMap; + +/** + * Event Model functions for APP4MC's {@link PeriodicSyntheticStimulus}. + * <p> + * For a full documentation of the underlying functions, please refer to the + * <a href="https://doi.org/10.1016/j.sysarc.2021.102343">related + * publication</a>, Section 3.4. + */ +public class EMPeriodicSynthetic implements IEventModel { + /** + * The actual period of this trigger pattern + */ + private Time tOuterPeriod; + /** + * List of activation events relative to the start of this pattern. + */ + private List<Time> lEntries; + + public EMPeriodicSynthetic() { + // Empty on purpose + } + + @Override + public long etaPlus(final Time dt) { + // Return 0 on dt=0 + if (dt.getValue().compareTo(BigInteger.ZERO) == 0) { + // For dt = 0, the upper and lower bound functions return 0 + return 0; + } + /* + * Calculate etaOuter, i.e. the number of releases per "full" outer period ( + * |T^Inner| * floor(dt/T^Outer) ) + */ + final double fraction = Math.floor(dt.divide(this.tOuterPeriod)); + final long iEtaOuter = (long) (this.lEntries.size() * fraction); + /* + * Calculate etaInner, i.e. the number of releases per "remainder" (dt mod + * T^Outer) + */ + // dt - T^Outer * floor(dt/T^Outer) + final Time rhs = this.tOuterPeriod.multiply(Math.floor(dt.divide(this.tOuterPeriod))); + final Time tRemainder = dt.subtract(rhs); + + // Check tRemainder (dt') + long iEtaInner = 0; + if (tRemainder.getValue().compareTo(BigInteger.ZERO) == 0) { + // No inner releases + iEtaInner = 0; + } else { + // More than one release: We need to find the **largest** n, for + // which the **minimum** of all deltaDash(n) is lower than dt' + + // We start by iterating over n (distance) + for (int n = 1; n <= this.lEntries.size(); n++) { + Time tDeltaDashMin = null; + // Iteration over the inner activation events (occurance times) + // and remember the lowest value + for (int j = 0; j < this.lEntries.size(); j++) { + if (null == tDeltaDashMin) { + tDeltaDashMin = deltaDash(n, j); + } else { + final Time a = tDeltaDashMin; + final Time b = deltaDash(n, j); + tDeltaDashMin = EventModelFactory.min(a, b); + } + } + // delta_dash(n) has to be lower than tRemainder + if (tDeltaDashMin != null && tDeltaDashMin.compareTo(tRemainder) < 0 && n > iEtaInner) { + iEtaInner = n; + } + } + } + + return iEtaOuter + iEtaInner; + } + + @Override + public long etaMinus(final Time dt) { + // Return 0 on dt=0 + if (dt.getValue().compareTo(BigInteger.ZERO) == 0) { + return 0; + } + /* + * Calculate etaOuter, i.e. the number of releases per "full" outer period ( + * |T^Inner| * floor(dt/T^Outer) ) + */ + final double fraction = Math.floor(dt.divide(this.tOuterPeriod)); + final long iEtaOuter = (long) (this.lEntries.size() * fraction); + + /* + * Calculate etaInner, i.e. the number of releases per "remainder" (dt mod + * T^Outer) + */ + // dt - T^Outer * floor(dt/T^Outer) + final Time rhs = this.tOuterPeriod.multiply(Math.floor(dt.divide(this.tOuterPeriod))); + final Time tRemainder = dt.subtract(rhs); + + // Check tRemainder (dt') + long iEtaInner = 0; + if (tRemainder.compareTo(deltaPlus(2)) < 0) { + // If dt' <= delta_plus(2), no activation events + iEtaInner = 0; + } else { + // We need to find the **largest** n, for + // which the **maximum** of all deltaDash(n) is lower than dt' + + // We start by iterating over n (distance) + for (long n = 1; n <= this.lEntries.size(); n++) { + Time tDeltaDashMax = null; + // Iteration over the inner activation events (occurance times) + for (int j = 0; j < this.lEntries.size(); j++) { + if (null == tDeltaDashMax) { + tDeltaDashMax = deltaDash(n, j); + } else { + final Time a = tDeltaDashMax; + final Time b = deltaDash(n, j); + tDeltaDashMax = EventModelFactory.max(a, b); + } + } + // delta_dash(n) has to be lower or equal to tRemainder + if (tDeltaDashMax != null && tDeltaDashMax.compareTo(tRemainder) <= 0 && n > iEtaInner) { + iEtaInner = n - 1; + } + } + } + + return iEtaOuter + iEtaInner; + } + + @Override + public Time deltaPlus(final long n) { + // Function is only valid for n >= 2, return null on invalid parameter + if (n < 2) { + return null; + } + + // Distance between full periods + // Integer division on purpose in order to round the result down + final long fullPeriodCount = (n - 1) / this.lEntries.size(); + final Time tFull = this.tOuterPeriod.multiply(fullPeriodCount); + + // Distance between "remaining" (not covered by full periods) releases + Time tPartial = null; + // The distance between the remaining k(n) releases is the minimum among + // all n releases + for (int j = 0; j < this.lEntries.size(); j++) { + final Time difference = deltaDash((n - 1) % this.lEntries.size() + 1, j); + + if (null == tPartial || difference.compareTo(tPartial) > 0) { + tPartial = difference; + } + } + + return tFull.add(tPartial); + } + + @Override + public Time deltaMinus(final long n) { + // Function is only valid for n >= 2, return null on invalid parameter + if (n < 2) { + return null; + } + + // Distance between full periods + // Integer division on purpose in order to round the result down + final long fullPeriodCount = (n - 1) / this.lEntries.size(); + final Time tFull = this.tOuterPeriod.multiply(fullPeriodCount); + + // Distance between "remaining" (not covered by full periods) releases + Time tPartial = null; + // The distance between the remaining k(n) releases is the minimum among + // all n releases + + for (int j = 0; j < this.lEntries.size(); j++) { + final Time difference = deltaDash((n - 1) % this.lEntries.size() + 1, j); + + if (null == tPartial || difference.compareTo(tPartial) < 0) { + tPartial = difference; + } + } + + return tFull.add(tPartial); + } + + /** + * Returns the exact distance between n events with the first event being j + * + * @param l + * @param j + * @return + */ + private Time deltaDash(final long l, final int j) { + final Time a = this.lEntries.get(j); + Time b; + if (j + k(l) >= this.lEntries.size()) { + // Carry over + final int index = j + k(l) - this.lEntries.size(); + b = this.lEntries.get(index); + b = b.add(this.tOuterPeriod); + } else { + b = this.lEntries.get(j + k(l)); + } + return b.subtract(a); + } + + /** + * Returns the nb. of events that are not covered by full periods (i.e. released + * as part of a periods fraction) + * + * @param l Number of total events + * @return Fraction of events + */ + private int k(final long l) { + assert (l > 0); + final int elementCount = this.lEntries.size(); + // Return the remainder, i.e. n % noElements + return (int) ((l - 1) - ((l - 1) / elementCount) * elementCount); + } + + @Override + public double getUtilization(Process process, TimeType tt, EMap<ModeLabel, String> modes) { + // Note that periodic synthetic stimuli are considered to be strictly periodic + // in APP4MC. Accordingly, the outer period is assumed to be constant (without + // any drift) and all activation events will be released with each outer period. + // Therefore, the load is only influenced by a process's execution time, thus + // making it unnecessary to handle different cases of periods. + final Time executionTime = RuntimeUtil.getExecutionTimeForProcess(process, modes, tt); + return executionTime.multiply(getEntries().size()).divide(getOuterPeriod()); + } + + public Time getOuterPeriod() { + return tOuterPeriod; + } + + public void setOuterPeriod(Time tOuterPeriod) { + this.tOuterPeriod = tOuterPeriod; + } + + public List<Time> getEntries() { + return lEntries; + } + + public void setEntries(List<Time> lEntries) { + this.lEntries = lEntries; + } +}
diff --git a/plugins/org.eclipse.app4mc.amalthea.model/src/org/eclipse/app4mc/amalthea/model/util/stimuli/EMSingle.java b/plugins/org.eclipse.app4mc.amalthea.model/src/org/eclipse/app4mc/amalthea/model/util/stimuli/EMSingle.java new file mode 100644 index 0000000..23b0c79 --- /dev/null +++ b/plugins/org.eclipse.app4mc.amalthea.model/src/org/eclipse/app4mc/amalthea/model/util/stimuli/EMSingle.java
@@ -0,0 +1,73 @@ +/** + ******************************************************************************** + * Copyright (c) 2022 Dortmund University of Applied Sciences and Arts 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: + * Dortmund University of Applied Sciences and Arts - initial API and implementation + ******************************************************************************** + */ + +package org.eclipse.app4mc.amalthea.model.util.stimuli; + +import java.math.BigInteger; + +import org.eclipse.app4mc.amalthea.model.ModeLabel; +import org.eclipse.app4mc.amalthea.model.Process; +import org.eclipse.app4mc.amalthea.model.SingleStimulus; +import org.eclipse.app4mc.amalthea.model.Time; +import org.eclipse.app4mc.amalthea.model.TimeUnit; +import org.eclipse.app4mc.amalthea.model.util.FactoryUtil; +import org.eclipse.app4mc.amalthea.model.util.RuntimeUtil.TimeType; +import org.eclipse.emf.common.util.EMap; + +/** + * Event Model functions for APP4MC's {@link SingleStimulus}. + * <p> + * For a full documentation of the underlying functions, please refer to the + * <a href="https://doi.org/10.1016/j.sysarc.2021.102343">related + * publication</a>, Section 3.5. + */ +public class EMSingle implements IEventModel { + // To the average human being, 292 Billion years should feel like infinity. + private static final Time INFINITY = FactoryUtil.createTime(Long.MAX_VALUE, TimeUnit.S); + + public EMSingle() { + // Empty on purpose + } + + @Override + public long etaPlus(final Time dt) { + if (dt.getValue().compareTo(BigInteger.ZERO) == 0) { + return 0; + } + return 1; + } + + @Override + public long etaMinus(final Time dt) { + return 0; + } + + @Override + public Time deltaPlus(final long n) { + return INFINITY; + } + + @Override + public Time deltaMinus(final long n) { + return INFINITY; + } + + @Override + public double getUtilization(Process process, TimeType tt, EMap<ModeLabel, String> modes) { + // The utilization of a process that is only activated once throughout an + // application's execution will converge towards 0. + return 0.0; + } +}
diff --git a/plugins/org.eclipse.app4mc.amalthea.model/src/org/eclipse/app4mc/amalthea/model/util/stimuli/EMVariableRate.java b/plugins/org.eclipse.app4mc.amalthea.model/src/org/eclipse/app4mc/amalthea/model/util/stimuli/EMVariableRate.java new file mode 100644 index 0000000..3b33c74 --- /dev/null +++ b/plugins/org.eclipse.app4mc.amalthea.model/src/org/eclipse/app4mc/amalthea/model/util/stimuli/EMVariableRate.java
@@ -0,0 +1,142 @@ +/** + ******************************************************************************** + * Copyright (c) 2022 Dortmund University of Applied Sciences and Arts 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: + * Dortmund University of Applied Sciences and Arts - initial API and implementation + ******************************************************************************** + */ + +package org.eclipse.app4mc.amalthea.model.util.stimuli; + +import org.eclipse.app4mc.amalthea.model.AmaltheaServices; +import org.eclipse.app4mc.amalthea.model.ModeLabel; +import org.eclipse.app4mc.amalthea.model.Process; +import org.eclipse.app4mc.amalthea.model.Time; +import org.eclipse.app4mc.amalthea.model.VariableRateStimulus; +import org.eclipse.app4mc.amalthea.model.util.RuntimeUtil; +import org.eclipse.app4mc.amalthea.model.util.RuntimeUtil.TimeType; +import org.eclipse.emf.common.util.EMap; + +/** + * Event Model functions for APP4MC's {@link VariableRateStimulus}. + */ +public class EMVariableRate implements IEventModel { + private Time tStep; + private double dLowerBoundOccurrences; + private double dUpperBoundOccurrences; + private double dAverageBoundOccurrences; + + public EMVariableRate() { + // Empty on purpose + } + + @Override + public long etaPlus(final Time dt) { + // Return 0 on dt=0 + if (EventModelFactory.isNullOrZero(dt)) { + // For dt = 0, the upper and lower bound functions return 0 + return 0; + } + + final double result = dt.multiply(this.dUpperBoundOccurrences).divide(this.tStep); + return (long) Math.ceil(result); + } + + @Override + public long etaMinus(final Time dt) { + // Return 0 on dt=0 + if (EventModelFactory.isNullOrZero(dt)) { + // For dt = 0, the upper and lower bound functions return 0 + return 0; + } + + final double result = dt.multiply(this.dLowerBoundOccurrences).divide(this.tStep); + // Equivalent to Math.floor + return (long) result; + } + + @Override + public Time deltaPlus(final long n) { + // Function is only valid for n >= 2, return null on invalid parameter + if (n < 2) { + return null; + } + + final double scale = (n - 1) / this.dUpperBoundOccurrences; + final Time tDistance = this.tStep.multiply(scale); + if (tDistance == null) { + return null; + } + return AmaltheaServices.adjustTimeUnit(tDistance); + } + + @Override + public Time deltaMinus(final long n) { + // Function is only valid for n >= 2, return null on invalid parameter + if (n < 2) { + return null; + } + + final double scale = (n - 1) / this.dLowerBoundOccurrences; + final Time tDistance = this.tStep.multiply(scale); + if (tDistance == null) { + return null; + } + return AmaltheaServices.adjustTimeUnit(tDistance); + } + + @Override + public double getUtilization(Process process, TimeType tt, EMap<ModeLabel, String> modes) { + final Time executionTime = RuntimeUtil.getExecutionTimeForProcess(process, modes, tt); + if (tt.equals(TimeType.BCET)) { + // Minimum load occurs when the minimum of activation events occurs per step + return executionTime.multiply(dLowerBoundOccurrences).divide(getStep()); + } else if (tt.equals(TimeType.WCET)) { + // Maximum load occurs when the maximum of activation events occurs per step + return executionTime.multiply(dUpperBoundOccurrences).divide(getStep()); + } else if (tt.equals(TimeType.ACET)) { + return executionTime.multiply(dAverageBoundOccurrences).divide(getStep()); + } + // Unsupported configuration + return -1; + } + + public Time getStep() { + return tStep; + } + + public void setStep(Time step) { + this.tStep = step; + } + + public double getLowerBoundOccurrences() { + return dLowerBoundOccurrences; + } + + public void setLowerBoundOccurrences(double dLowerBoundOccurrences) { + this.dLowerBoundOccurrences = dLowerBoundOccurrences; + } + + public double getAverageBoundOccurrences() { + return dAverageBoundOccurrences; + } + + public void setAverageBoundOccurrences(double dAverageBoundOccurrences) { + this.dAverageBoundOccurrences = dAverageBoundOccurrences; + } + + public double getUpperBoundOccurrences() { + return dUpperBoundOccurrences; + } + + public void setUpperBoundOccurrences(double dUpperBoundOccurrences) { + this.dUpperBoundOccurrences = dUpperBoundOccurrences; + } +}
diff --git a/plugins/org.eclipse.app4mc.amalthea.model/src/org/eclipse/app4mc/amalthea/model/util/stimuli/EventModelFactory.java b/plugins/org.eclipse.app4mc.amalthea.model/src/org/eclipse/app4mc/amalthea/model/util/stimuli/EventModelFactory.java new file mode 100644 index 0000000..d8bcf1b --- /dev/null +++ b/plugins/org.eclipse.app4mc.amalthea.model/src/org/eclipse/app4mc/amalthea/model/util/stimuli/EventModelFactory.java
@@ -0,0 +1,206 @@ +/** + ******************************************************************************** + * Copyright (c) 2022 Dortmund University of Applied Sciences and Arts 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: + * Dortmund University of Applied Sciences and Arts - initial API and implementation + ******************************************************************************** + */ + +package org.eclipse.app4mc.amalthea.model.util.stimuli; + +import java.math.BigInteger; + +import org.eclipse.app4mc.amalthea.model.PeriodicBurstStimulus; +import org.eclipse.app4mc.amalthea.model.PeriodicStimulus; +import org.eclipse.app4mc.amalthea.model.PeriodicSyntheticStimulus; +import org.eclipse.app4mc.amalthea.model.RelativePeriodicStimulus; +import org.eclipse.app4mc.amalthea.model.SingleStimulus; +import org.eclipse.app4mc.amalthea.model.Stimulus; +import org.eclipse.app4mc.amalthea.model.Time; +import org.eclipse.app4mc.amalthea.model.VariableRateStimulus; +import org.eclipse.app4mc.amalthea.model.util.FactoryUtil; + +public class EventModelFactory { + /** + * Constant representing a Time object corresponding to 0ms. + */ + private static final Time ZEROMS = FactoryUtil.createTime("0ms"); + + // Suppress default constructor + private EventModelFactory() { + throw new IllegalStateException("Utility class"); + } + + public static IEventModel of(final Stimulus s) { + if (s instanceof PeriodicStimulus) { + return ofStimuli((PeriodicStimulus) s); + } else if (s instanceof PeriodicSyntheticStimulus) { + return ofStimuli((PeriodicSyntheticStimulus) s); + } else if (s instanceof RelativePeriodicStimulus) { + return ofStimuli((RelativePeriodicStimulus) s); + } else if (s instanceof PeriodicBurstStimulus) { + return ofStimuli((PeriodicBurstStimulus) s); + } else if (s instanceof SingleStimulus) { + return ofStimuli((SingleStimulus) s); + } else if (s instanceof VariableRateStimulus) { + return ofStimuli((VariableRateStimulus) s); + } + // Stimulus is not supported + return null; + } + + /********************************************************* + * Factory methods for individual APP4MC stimuli + *********************************************************/ + + private static IEventModel ofStimuli(PeriodicStimulus s) { + EMPeriodic em = new EMPeriodic(); + em.setRecurrence(s.getRecurrence()); + /* + * Check if minDistance is set. If not, we are safe to initialize this variable + * with a dummy value of zero, as it will only be used as part of max functions + * (i.e. to make sure a lower bound on the distance is not exceeded) + */ + if (s.getMinDistance() == null) { + em.setMinDistance(FactoryUtil.createTime("0ms")); + } else { + em.setMinDistance(s.getMinDistance()); + } + + /** + * Check if jitter is is set. If not, we are safe to initialize this variable + * with a dummy value of zero, as it will only be used as in additions / + * subtractions. + */ + if (s.getJitter() == null) { + em.setJitter(FactoryUtil.createTime("0ms")); + } else { + em.setJitter(s.getJitter().getUpperBound()); + } + return em; + } + + private static IEventModel ofStimuli(PeriodicSyntheticStimulus s) { + EMPeriodicSynthetic em = new EMPeriodicSynthetic(); + em.setOuterPeriod(s.getRecurrence()); + em.setEntries(s.getOccurrenceTimes()); + return em; + } + + private static IEventModel ofStimuli(RelativePeriodicStimulus periodicRelative) { + EMPeriodicRelative em = new EMPeriodicRelative(); + em.setLowerBoundDistance(periodicRelative.getNextOccurrence().getLowerBound()); + em.setUpperBoundDistance(periodicRelative.getNextOccurrence().getUpperBound()); + return em; + } + + private static IEventModel ofStimuli(PeriodicBurstStimulus s) { + EMPeriodicBurst em = new EMPeriodicBurst(); + + // Feasibility check to prevent the creation of an invalid object that would + // cause exceptions. + if (EventModelFactory.isNullOrZero(s.getRecurrence()) || EventModelFactory.isNullOrZero(s.getBurstLength())) { + return null; + } + + // Set Mandatory parameters + em.setOuterPeriod(s.getRecurrence()); + em.setBurstLength(s.getBurstLength()); + + /* + * From APP4MC Help: The number of occurrences per burst are specified via + * occurrenceCount. The occurrenceMinDinstance defines the minimal distance + * between them. The burstLength defines the maximum time the burst pattern can + * last. If the number of occurrences multiplied with the minimum distance + * between activations is bigger than the burstLength only the number of + * activations that fit into the burstLength are executed. + */ + + // Check whether or not OccurrenceCount * OccurrenceMinDistance < + // BurstLength, and limit the number of events accordingly. + // if tMinDistance != 0 && tMinDistance * (occurranceCount - 1) > + // tBurstLength + if (!EventModelFactory.isNullOrZero(s.getOccurrenceMinDistance()) + && !EventModelFactory.isNullOrZero(em.getBurstLength()) && s.getOccurrenceMinDistance() + .multiply(s.getOccurrenceCount() - (long) 1).compareTo(em.getBurstLength()) > 0) { + // OccurrenceCount * OccurrenceMinDistance > BurstLength, i.e. limit + // nb. of occurrences + em.setOccurrenceCount((int) Math.ceil(em.getBurstLength().divide(s.getOccurrenceMinDistance()))); + } else { + // BurstLength is either not set, or does not constrain the nb. of + // Occurrences + em.setOccurrenceCount(s.getOccurrenceCount()); + } + + // Initialize optional parameter + if (s.getOccurrenceMinDistance() != null) { + em.setMinDistance(s.getOccurrenceMinDistance()); + em.setDeltaBurst(em.getBurstLength().subtract(em.getMinDistance().multiply(em.getOccurrenceCount() - 1))); + } else { + em.setMinDistance(ZEROMS); + em.setDeltaBurst(ZEROMS); + } + return em; + } + + @java.lang.SuppressWarnings("squid:S1172") + private static IEventModel ofStimuli(SingleStimulus singleStimulus) { + // Warnings disabled on purpose, since it is intended that neither + // singleStimulus nor em are used within this method. + @java.lang.SuppressWarnings("squid:S1488") + EMSingle em = new EMSingle(); + return em; + } + + private static IEventModel ofStimuli(VariableRateStimulus s) { + EMVariableRate em = new EMVariableRate(); + em.setStep(s.getStep()); + em.setLowerBoundOccurrences(s.getOccurrencesPerStep().getLowerBound()); + em.setUpperBoundOccurrences(s.getOccurrencesPerStep().getUpperBound()); + em.setAverageBoundOccurrences(s.getOccurrencesPerStep().getAverage()); + return em; + } + + /********************************************************* + * Utility functions used to support arithmetic operations + *********************************************************/ + + /** + * Returns the maximum between <a href="#{@link}">{@link Time}</a> a and b + * + * @param a + * @param b + * @return + */ + public static Time max(final Time a, final Time b) { + return (a.compareTo(b) > 0) ? a : b; + } + + /** + * Returns the minimum between <a href="#{@link}">{@link Time}</a> a and b + * + * @param a + * @param b + * @return + */ + public static Time min(final Time a, final Time b) { + return (a.compareTo(b) < 0) ? a : b; + } + + /** + * Returns {@code True} if the given {@link Time} is either {@code Null} or 0. + * + * @param t + * @return + */ + public static boolean isNullOrZero(final Time t) { + return (t == null || t.getValue().compareTo(BigInteger.ZERO) == 0); + } +}
diff --git a/plugins/org.eclipse.app4mc.amalthea.model/src/org/eclipse/app4mc/amalthea/model/util/stimuli/IEventModel.java b/plugins/org.eclipse.app4mc.amalthea.model/src/org/eclipse/app4mc/amalthea/model/util/stimuli/IEventModel.java new file mode 100644 index 0000000..b507649 --- /dev/null +++ b/plugins/org.eclipse.app4mc.amalthea.model/src/org/eclipse/app4mc/amalthea/model/util/stimuli/IEventModel.java
@@ -0,0 +1,72 @@ +/** + ******************************************************************************** + * Copyright (c) 2022 Dortmund University of Applied Sciences and Arts 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: + * Dortmund University of Applied Sciences and Arts - initial API and implementation + ******************************************************************************** + */ + +package org.eclipse.app4mc.amalthea.model.util.stimuli; + +import org.eclipse.app4mc.amalthea.model.ModeLabel; +import org.eclipse.app4mc.amalthea.model.Process; +import org.eclipse.app4mc.amalthea.model.Time; +import org.eclipse.app4mc.amalthea.model.util.RuntimeUtil.TimeType; +import org.eclipse.emf.common.util.EMap; + +public interface IEventModel { + /** + * Returns the upper bound on the number of times a process is released in the + * <a href="#{@link}">{@link Time}</a> interval dt. + * + * @param dt size of the time interval + * @return maximum number of times a process is released + */ + public long etaPlus(Time dt); + + /** + * Returns the lower bound on the number of times a process is released in the + * <a href="#{@link}">{@link Time}</a> interval dt. + * + * @param dt size of the time interval + * @return minimum number of times a process is released + */ + public long etaMinus(Time dt); + + /** + * Returns the upper bound on the distance between n activation events. + * + * @param n number of activation events + * @return maximum distance between activation events + */ + public Time deltaPlus(long n); + + /** + * Returns the lower bound on the distance between n activation events. + * + * @param n number of activation events + * @return minimum distance between activation events + */ + public Time deltaMinus(long n); + + /** + * Returns the {@link TimeType} utilization of the given {@link Process} when + * activated using the given {@link IEventModel}. + * + * @param process The process for which utilization shall be calculated for. + * @param tt Type of the utilization bound (worst, average, or best case + * assumption) to calculate. + * @param modes What modes to consider during utilization calculation (use + * {@code null} to disable filtering and consider all operation + * modes) + * @return + */ + public double getUtilization(Process process, TimeType tt, EMap<ModeLabel, String> modes); +}