| /** |
| ******************************************************************************** |
| * Copyright (c) 2020-2021 Robert Bosch GmbH. |
| * |
| * 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.slg.linux.generators |
| |
| import java.util.HashMap |
| import java.util.Properties |
| import org.eclipse.app4mc.amalthea.model.LabelAccess |
| import org.eclipse.app4mc.amalthea.model.MinAvgMaxStatistic |
| import org.eclipse.app4mc.amalthea.model.Runnable |
| import org.eclipse.app4mc.amalthea.model.SingleValueStatistic |
| import org.eclipse.app4mc.amalthea.model.StringObject |
| import org.eclipse.app4mc.amalthea.model.Ticks |
| import org.eclipse.app4mc.amalthea.model.impl.CustomPropertyImpl |
| import org.eclipse.app4mc.slg.config.ConfigModel |
| import org.eclipse.app4mc.slg.config.PlatformArchitecture |
| |
| class LinuxRunnableGenerator { |
| |
| // Suppress default constructor |
| private new() { |
| throw new IllegalStateException("Utility class"); |
| } |
| |
| def static String snippetSrcStart(ConfigModel configModel) |
| ''' |
| #include "runnables.h" |
| #include "codesnippets.h" |
| |
| «val PlatformArchitecture platformArchitectureType = configModel.getPlatformArchitectureType()» |
| «IF platformArchitectureType!==null» #define __«platformArchitectureType.getName»__«ENDIF» |
| |
| void ticks(int numberTicks){ |
| # if defined (__x86_64__) |
| for(int i = 0; i < numberTicks; i++){ |
| «FOR k : 1..400» |
| __asm volatile("nop"); |
| «ENDFOR» |
| } |
| # elif defined (__x86_32__) |
| for(int i = 0; i < numberTicks; i++){ |
| «FOR k : 1..400» |
| __asm volatile("mov r0, r0"); |
| «ENDFOR» |
| } |
| # elif defined (__aarch64__) |
| for(int i = 0; i < numberTicks; i++){ |
| «FOR k : 1..400» |
| __asm volatile("mov x0, x0"); |
| «ENDFOR» |
| } |
| #endif |
| } |
| ''' |
| |
| def static String snippetSrcBody(Runnable runnable, String codeString, boolean enableExtCode) |
| ''' |
| //Runnable «runnable?.name» |
| void run_«runnable?.name»(char* coreName){ |
| «codeString» |
| } |
| ''' |
| |
| def static String snippetIncStart() |
| ''' |
| #include "codesnippets.h" |
| |
| void ticks(int numberTicks); |
| ''' |
| |
| /** |
| * This method is used to insert Synthetic code by considering the label-access's, Ticks (default and the PU specific i.e. extended ones) |
| */ |
| static def String syntheticLoad(Runnable runnable, boolean experimental, Properties properties, boolean enableExtCode ) { |
| |
| val calc=new Calculation(); |
| |
| val StringBuffer codeHookFunctionsBuffer = new StringBuffer // custom property |
| val StringBuffer codeHookFunctionsOverwriteBuffer = new StringBuffer // custom property with overwrite |
| updateContent(runnable, calc, properties, codeHookFunctionsBuffer, codeHookFunctionsOverwriteBuffer) |
| |
| val buffer = ''' |
| «IF enableExtCode» |
| «IF codeHookFunctionsOverwriteBuffer.length() == 0» |
| |
| «codeHookFunctionsBuffer» |
| «ELSE» |
| |
| «codeHookFunctionsOverwriteBuffer» |
| «ENDIF» |
| «ENDIF» |
| «IF !enableExtCode || codeHookFunctionsOverwriteBuffer.length() == 0» ««« // write the runnable's code either when there is no external code or when the codehook is not of the overwrite type. |
| «var extendedFound=false» |
| «FOR puName : calc.ticksSumMap.keySet.sort» |
| «IF !puName.equals("default")» |
| «IF extendedFound »else«ENDIF» if(strcmp(coreName,"«puName»")==0){ |
| «syntheticLoadContentForEachPU( experimental, calc,puName)» |
| } |
| «{extendedFound=true;""}» |
| «ENDIF» |
| «ENDFOR» |
| «IF calc.ticksSumMap.containsKey("default")» |
| |
| «val value=syntheticLoadContentForEachPU( experimental, calc,"default" )» |
| «IF value!==null && value.length>0» |
| «IF extendedFound »else«ENDIF» |
| { |
| «value» |
| } |
| |
| «ENDIF» |
| «ENDIF» |
| «ENDIF» |
| ''' |
| return buffer |
| |
| } |
| |
| def static updateContent(Runnable runnable, Calculation calculation, Properties properties, StringBuffer codehookFct, StringBuffer codehookFctOverwrite) { |
| |
| calculation.ticksSumMap.put("default", 0) |
| |
| runnable?.activityGraph?.eContents?.forEach [ item | |
| |
| |
| if (item instanceof Ticks) { |
| calculation.ticksSumMap.put("default", calculation.ticksSumMap.get("default") + item?.^default?.average?.intValue) |
| |
| val puDefinitions = item?.extended?.keySet |
| |
| for (puDefinition : puDefinitions) { |
| if (puDefinition !== null) { |
| |
| if (!calculation.ticksSumMap.containsKey(puDefinition.name)) { |
| calculation.ticksSumMap.put(puDefinition.name, 0) |
| } |
| calculation.ticksSumMap.put(puDefinition.name, calculation.ticksSumMap.get(puDefinition.name) + |
| item?.extended?.get(puDefinition)?.average?.intValue) |
| |
| } |
| |
| } |
| } else if (item instanceof LabelAccess) { |
| |
| var Float value = Float.parseFloat( |
| properties.getOrDefault("labelAccessStatisticValueDefault", "1.0F").toString) |
| |
| val labelStatistic = item.statistic |
| |
| if (labelStatistic !== null) { |
| |
| var labelStatisticValue = labelStatistic.value |
| |
| if (labelStatisticValue instanceof SingleValueStatistic) { |
| value = labelStatisticValue.value |
| } else if (labelStatisticValue instanceof MinAvgMaxStatistic) { |
| // TODO: provide a configuration option, to select appropriate value from labelStatistic (min/max/avg) |
| // right now considering the average value |
| value = labelStatisticValue.avg |
| |
| } |
| |
| } |
| if (item.access.toString == "read") { |
| calculation.readsSum += value.intValue |
| } else if (item.access.toString == "write") { |
| calculation.writesSum += value.intValue |
| |
| } |
| } |
| else if (item instanceof CustomPropertyImpl) // custom property |
| { |
| if (item.getKey.equals("codehook_overwrite")){ |
| |
| val value = item.getValue |
| if(value instanceof StringObject){ |
| codehookFctOverwrite.append((value.value)+ ";") |
| codehookFctOverwrite.append(System.getProperty("line.separator")) |
| codehookFctOverwrite.append(System.getProperty("line.separator")) |
| } |
| // println(item.value) |
| } |
| |
| else if (item.getKey.equals("codehook")){ |
| |
| val value = item.getValue |
| if(value instanceof StringObject){ |
| codehookFct.append((value.value)+ ";") |
| codehookFct.append(System.getProperty("line.separator")) |
| codehookFct.append(System.getProperty("line.separator")) |
| |
| } |
| // println(item.value) |
| } |
| |
| } |
| ] |
| } |
| |
| static private def String syntheticLoadContentForEachPU( boolean experimental, Calculation calc,String puName) { |
| |
| calc.resetCalculatedTicksSum |
| |
| calc.calculatedTicksSum = calc.ticksSumMap.get(puName) |
| |
| if (calc.readsSum !== 0 && calc.readsSum < 10) { |
| calc.readsSum = 10 |
| } |
| if (calc.writesSum != 0 && calc.writesSum < 10) { |
| calc.writesSum = 10 |
| } |
| |
| calc.matchingCodeSnippetIdsBuffer = "" |
| performCodeSnippetMatching(experimental, calc) |
| |
| // At this level 'matchingCodeSnippetIds' variable-> has the names of the algorithms which are matching to the given criteria of the Runnable |
| calc.calculatedTicksSum -= (calc.writesSum * 1.0171).intValue |
| calc.calculatedTicksSum -= (calc.readsSum * 1.0067).intValue |
| calc.calculatedTicksSum /= 400 |
| |
| calc.readsSum /= 10 |
| calc.writesSum /= 10 |
| |
| if (calc.calculatedTicksSum> 0 && calc.matchingCodeSnippetIdsBuffer.length == 0) { |
| /* |
| * This code is executed if no code snippet is matching to the criteria of the Runnable (ticks/load/store) |
| * Only ATOMIC code is generated |
| */ |
| caseWithOnlyTicks(calc) |
| return (calc.matchingCodeSnippetIdsBuffer) |
| } else if ((calc.calculatedTicksSum <= 0 && |
| (calc.writesSum > 0 || calc.readsSum > 0)) && |
| calc.matchingCodeSnippetIdsBuffer.length == 0) { |
| /* |
| * This code is executed if no code snippet is matching to the criteria of the Runnable (ticks/load/store) |
| * Only ATOMIC code is generated |
| */ |
| caseWithOutTicksButWithLabelAccesses(calc) |
| return (calc.matchingCodeSnippetIdsBuffer) |
| } |
| if (calc.calculatedTicksSum > 0 && calc.matchingCodeSnippetIdsBuffer.length != 0) { |
| /* |
| * This code is executed when certain code snippets matching to the criteria of the Runnable(ticks/load/store) are found. |
| */ |
| caseWithTicksAndMatchingCodeSnippets(calc) |
| return (calc.matchingCodeSnippetIdsBuffer) |
| } |
| } |
| |
| /** |
| * This method is used to check if the load specified at the Runnable is matching with any of the pre-defined code-snippet |
| */ |
| static private def void performCodeSnippetMatching(boolean experimental, Calculation calc) { |
| var int i |
| var boolean added = false |
| var codesnippets = LinuxRealisiticSyntheticGenerator.getCodeSnippets() |
| |
| // CodeSnippet matching algorithm is enabled only if the switch 'experimentalCodeSnippetMatching' is supplied |
| if (experimental) { // properties.getProperty("experimentalCodeSnippetMatching") |
| |
| for (i = 0; i < codesnippets.length; i++) { |
| if (i == 0) { |
| added = false |
| } |
| var int ticks_net = codesnippets.get(i).ticks - codesnippets.get(i).stallsload - |
| codesnippets.get(i).stallsstore |
| |
| // This code is used to check which code snippets matches to the given Runnable (based on the ticks) |
| if (ticks_net < calc.calculatedTicksSum && |
| codesnippets.get(i).reads < calc.readsSum && |
| codesnippets.get(i).writes < calc.writesSum) { |
| calc.matchingCodeSnippetIdsBuffer += codesnippets.get(i).name + "();/\n"+System.getProperty("line.separator") |
| calc.calculatedTicksSum -= ticks_net |
| calc.readsSum -= codesnippets.get(i).reads |
| calc.writesSum -= codesnippets.get(i).writes |
| added = true |
| } |
| if (added && i == codesnippets.length - 1) { |
| i = -1 |
| } |
| } |
| } |
| } |
| |
| /** |
| * This method is used to generate the synthetic code for Runnable in the case when Ticks are present, and the 'synthetic code snippets are matching' |
| */ |
| static private def void caseWithTicksAndMatchingCodeSnippets(Calculation calc) { |
| var String[] matchingCodeSnippetsIdsArray = calc.matchingCodeSnippetIdsBuffer.split("/") |
| |
| var int numberOfCodeSnippets = matchingCodeSnippetsIdsArray.length |
| |
| calc.writesSum /= numberOfCodeSnippets |
| calc.readsSum /= numberOfCodeSnippets |
| calc.calculatedTicksSum /= numberOfCodeSnippets |
| |
| calc.matchingCodeSnippetIdsBuffer = ''' |
| volatile int arraymb[250000]; |
| int i; |
| ''' |
| matchingCodeSnippetsIdsArray.forEach [ codeSumPart | |
| // inserting code snippet call e.g: bubblesort(); |
| calc.matchingCodeSnippetIdsBuffer += codeSumPart |
| |
| var int randomNumber1 = (Math.random() * 200000).intValue |
| var int randomNumber2 = (Math.random() * 200000).intValue |
| |
| calc.matchingCodeSnippetIdsBuffer += ''' |
| «IF calc.writesSum > 0 || calc.readsSum > 0» |
| «IF calc.writesSum > 0» |
| for (i = 0; i < «calc.writesSum»; i++){ |
| «FOR k : 0..9» |
| arraymb[«(Math.random()*5000).intValue»] = «randomNumber1»; |
| «ENDFOR» |
| } |
| «ENDIF» |
| «IF calc.readsSum > 0» |
| for (i = 0; i < «calc.readsSum»; i++){ |
| «FOR k : 0..9» |
| arraymb[«(Math.random()*5000).intValue»] == «randomNumber2»; |
| «ENDFOR» |
| } |
| «ENDIF» |
| «ENDIF» |
| ticks(«calc.calculatedTicksSum»); |
| ''' |
| ] |
| } |
| |
| /** |
| * This method is used to generate the synthetic code for Runnable in the case when Ticks and LabelAccess's are present |
| */ |
| static private def String caseWithOutTicksButWithLabelAccesses(Calculation calc) { |
| var int randomNumber1 = (Math.random() * 200000).intValue |
| var int randomNumber2 = (Math.random() * 200000).intValue |
| calc.matchingCodeSnippetIdsBuffer = ''' |
| «IF calc.writesSum > 0 || calc.readsSum > 0» |
| volatile int arraymb[250000]; |
| int i; |
| «IF calc.writesSum > 0» |
| for (i = 0; i < «calc.writesSum»; i++){ |
| «FOR k : 0..9» |
| arraymb[«(Math.random()*5000).intValue»] = «randomNumber1»; |
| «ENDFOR» |
| } |
| «ENDIF» |
| «IF calc.readsSum > 0» |
| for (i = 0; i < «calc.readsSum»; i++){ |
| «FOR k : 0..9» |
| arraymb[«(Math.random()*5000).intValue»] == «randomNumber2»; |
| «ENDFOR» |
| } |
| «ENDIF» |
| «ENDIF» |
| ''' |
| } |
| |
| /** |
| * This method is used to generate the synthetic code for Runnable in the case when only Ticks are present |
| */ |
| static private def String caseWithOnlyTicks(Calculation calc) { |
| var int randomNumber1 = (Math.random() * 200000).intValue |
| var int randomNumber2 = (Math.random() * 200000).intValue |
| calc.matchingCodeSnippetIdsBuffer = ''' |
| «IF calc.writesSum > 0 || calc.readsSum > 0» |
| volatile int arraymb[250000]; |
| int i; |
| «IF calc.writesSum > 0» |
| for (i = 0; i < «calc.writesSum»; i++){ |
| «FOR k : 0..9» |
| arraymb[«(Math.random()*5000).intValue»] = «randomNumber1»; |
| «ENDFOR» |
| } |
| «ENDIF» |
| «IF calc.readsSum > 0» |
| for (i = 0; i < «calc.readsSum»; i++){ |
| «FOR k : 0..9» |
| arraymb[«(Math.random()*5000).intValue»] == «randomNumber2»; |
| «ENDFOR» |
| } |
| «ENDIF» |
| «ENDIF» |
| ticks(«calc.calculatedTicksSum»); |
| ''' |
| } |
| |
| static class Calculation { |
| public var int calculatedTicksSum = 0 |
| |
| public var int readsSum = 0 // extracted from runnable |
| public var int writesSum = 0 // extracted from runnable |
| |
| public var String matchingCodeSnippetIdsBuffer = "" |
| |
| public val ticksSumMap = new HashMap<String, Integer> |
| |
| def resetCalculatedTicksSum(){ |
| calculatedTicksSum=0; |
| } |
| } |
| |
| } |