| /** |
| * ******************************************************************************* |
| * Copyright (c) 2019-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.amlt2systemc.m2t.transformers |
| |
| import com.google.inject.Inject |
| import com.google.inject.Singleton |
| import org.eclipse.app4mc.amalthea.model.Amalthea |
| import org.eclipse.app4mc.amalthea.model.util.ModelUtil |
| import org.eclipse.app4mc.amlt2systemc.m2t.module.BaseTransformer |
| import org.eclipse.app4mc.amlt2systemc.m2t.module.PropertyKeys |
| import org.eclipse.app4mc.amlt2systemc.m2t.module.PropertyKeys.TraceType |
| import org.eclipse.app4mc.amlt2systemc.m2t.transformers.event.EventModelTransformer |
| import org.eclipse.app4mc.amlt2systemc.m2t.transformers.hw.HwModelTransformer |
| import org.eclipse.app4mc.amlt2systemc.m2t.transformers.hw.MemoryTransformer |
| import org.eclipse.app4mc.amlt2systemc.m2t.transformers.hw.ProcessingUnitTransformer |
| import org.eclipse.app4mc.amlt2systemc.m2t.transformers.mapping.MappingModelTransformer |
| import org.eclipse.app4mc.amlt2systemc.m2t.transformers.os.OsModelTransformer |
| import org.eclipse.app4mc.amlt2systemc.m2t.transformers.stimuli.StimuliModelTransformer |
| import org.eclipse.app4mc.amlt2systemc.m2t.transformers.sw.SWModelTransformer |
| import org.eclipse.app4mc.amlt2systemc.m2t.utils.TuSort |
| import org.eclipse.app4mc.transformation.util.OutputBuffer |
| import org.eclipse.app4mc.util.sessionlog.SessionLogger |
| import java.nio.file.Paths |
| |
| @Singleton |
| class AmaltheaTransformer extends BaseTransformer { |
| |
| @Inject OutputBuffer outputBuffer |
| @Inject SWModelTransformer swModelTransformer |
| @Inject OsModelTransformer osModelTransformer |
| @Inject MappingModelTransformer mappingModelTransformer |
| @Inject HwModelTransformer hwModelTransformer |
| @Inject EventModelTransformer eventModelTransformer |
| @Inject StimuliModelTransformer stimuliModelTransformer |
| |
| @Inject ProcessingUnitTransformer processingUnitTransformer |
| @Inject MemoryTransformer memoryTransformer |
| |
| @Inject SessionLogger logger; |
| |
| static def getModulePath() { |
| return "amaltheaTop" |
| } |
| |
| def getModuleName() { |
| return getModulePath() + "/amalthea" |
| } |
| |
| def void transform(Amalthea[] amaltheas) { |
| if (amaltheas === null || amaltheas.empty) { |
| logger.error("Input Amalthea model(s) invalid (received null).") |
| return |
| } |
| |
| // make sure all submodels are present in at least one input model |
| // Note: chose index 0 at will, any list element would do just fine |
| adjustModel(amaltheas.get(0)) |
| hwModelTransformer.transform(amaltheas.map[it.hwModel]) |
| swModelTransformer.transform(amaltheas.map[it.swModel]) |
| mappingModelTransformer.transform(amaltheas.map[it.mappingModel]) |
| osModelTransformer.transform(amaltheas.map[it.osModel]) |
| eventModelTransformer.transform(amaltheas.map[it.eventModel]) |
| stimuliModelTransformer.transform(amaltheas.map[it.stimuliModel]) |
| |
| outputBuffer.appendTo("INC", getModuleName(), toH()) |
| outputBuffer.appendTo("SRC", getModuleName(), toCpp()) // swModels, hwModels, mappingModels, osModels, eventModels, stimuliModels)) |
| outputBuffer.appendTo("OTHER", getModulePath() + "/CMakeLists.txt", getCMake()); |
| outputBuffer.appendTo("OTHER", "/build.bat", getBuildScript()); |
| outputBuffer.appendTo("OTHER", "/run.bat", getRunScript()); |
| |
| } |
| |
| private def void adjustModel(Amalthea amalthea) { |
| // ensure that the following sub models are available |
| ModelUtil.getOrCreateSwModel(amalthea); |
| ModelUtil.getOrCreateOsModel(amalthea); |
| ModelUtil.getOrCreateHwModel(amalthea); |
| ModelUtil.getOrCreateMappingModel(amalthea); |
| ModelUtil.getOrCreateEventModel(amalthea); |
| ModelUtil.getOrCreateStimuliModel(amalthea); |
| } |
| |
| private def String toH() ''' |
| //top level header |
| ''' |
| |
| private def String toCpp() ''' |
| #include "APP4MCsim.h" |
| #include "Tracer/TracerFactory.h" |
| #include "SimParamParser.h" |
| |
| #include "«getModuleName()».h" |
| //include model elements |
| «FOR obj : TuSort.byModule(hwModelTransformer.cache.values)» |
| #include "«obj.module».h" |
| «ENDFOR» |
| «FOR obj : TuSort.byModule(swModelTransformer.cache.values)» |
| #include "«obj.module».h" |
| «ENDFOR» |
| «FOR obj : TuSort.byModule(osModelTransformer.cache.values)» |
| #include "«obj.module».h" |
| «ENDFOR» |
| «FOR obj : TuSort.byModule(mappingModelTransformer.cache.values)» |
| #include "«obj.module».h" |
| «ENDFOR» |
| «FOR obj : TuSort.byModule(eventModelTransformer.cache.values)» |
| #include "«obj.module».h" |
| «ENDFOR» |
| «FOR obj : TuSort.byModule(stimuliModelTransformer.cache.values)» |
| #include "«obj.module».h" |
| «ENDFOR» |
| |
| |
| //include processing units and memories for tracing ----- |
| «FOR tu : processingUnitTransformer.cache.values» |
| #include "«tu.module».h" |
| «ENDFOR» |
| «FOR tu : memoryTransformer.cache.values» |
| #include "«tu.module».h" |
| «ENDFOR» |
| // tracing includes ----------------- |
| |
| INITIALIZE_EASYLOGGINGPP; |
| |
| int sc_main(int argc, char *argv[]) { |
| //static code ---------------------- |
| sc_core::sc_set_time_resolution(1.0,sc_core::SC_NS); |
| //throw error if object already exists, default behavior will rename second object and merely issue a warning (W505) |
| sc_core::sc_report_handler::set_actions("object already exists",sc_core::SC_THROW); |
| START_EASYLOGGINGPP(argc, argv); |
| SimParam params; |
| SimParamParser::parse(argc, argv, params); |
| //end static code ------------------ |
| |
| VLOG(0) << "build simulation model"; |
| /* Hardware */ |
| //always initialize hardware model first, as it relies on |
| //hierarchical top->down construction, cross-references |
| //from other models' instantiation might interfere with that pattern |
| «FOR obj : TuSort.byModule(hwModelTransformer.cache.values)» |
| «obj.call»; |
| «ENDFOR» |
| /* Software */ |
| «FOR obj : TuSort.byModule(swModelTransformer.cache.values)» |
| «obj.call»; |
| «ENDFOR» |
| /* OS */ |
| «FOR obj : TuSort.byModule(osModelTransformer.cache.values)» |
| «obj.call»; |
| «ENDFOR» |
| /* Mapping */ |
| «FOR obj : TuSort.byModule(mappingModelTransformer.cache.values)» |
| «obj.call»; |
| «ENDFOR» |
| /* Event */ |
| «FOR obj : TuSort.byModule(eventModelTransformer.cache.values)» |
| «obj.call»; |
| «ENDFOR» |
| /* Stimuli */ |
| «FOR obj : TuSort.byModule(stimuliModelTransformer.cache.values)» |
| «obj.call»; |
| «ENDFOR» |
| VLOG(0) << "simulation model complete"; |
| |
| //static code ---------------------- |
| auto vcdPath = TraceManager::createTraceFilePath("trace").string(); |
| sc_core::sc_trace_file *tf = sc_core::sc_create_vcd_trace_file(vcdPath.c_str()); |
| |
| //trace processing units and memories ----- |
| «FOR tu : processingUnitTransformer.cache.values» |
| sc_trace(tf, &*«tu.call»(), "«tu.module.replaceAll("/", "_")»"); |
| «ENDFOR» |
| «FOR tu : memoryTransformer.cache.values» |
| sc_trace(tf, &*«tu.call»(), "«tu.module.replaceAll("/", "_")»"); |
| «ENDFOR» |
| |
| |
| //run simulation |
| try { |
| VLOG(0) << "starting simulation"; |
| sc_core::sc_start(params.simulationTimeInMs, sc_core::SC_MS); |
| } |
| catch (sc_core::sc_report e) { |
| const char* file = e.get_file_name(); |
| const int line = e.get_line_number(); |
| const char* msg = e.get_msg(); |
| VLOG(0) << msg << "\nin file: " << file << "\nline: " << line; |
| return -1; |
| } |
| |
| sc_core::sc_close_vcd_trace_file(tf); |
| |
| VLOG(0) << "simulation done "; |
| return 0; |
| } |
| ''' |
| |
| def String getCMake() ''' |
| include_directories(${PROJECT_SOURCE_DIR}) |
| |
| add_executable («getProperty(PropertyKeys.PROJECT_NAME)» |
| "${PROJECT_SOURCE_DIR}/«getModuleName()».cpp" |
| ) |
| |
| target_link_libraries(«getProperty(PropertyKeys.PROJECT_NAME)» app4mc.sim_lib) |
| target_include_directories(«getProperty(PropertyKeys.PROJECT_NAME)» PUBLIC app4mc.sim_lib) |
| |
| add_subdirectory ("./hwModel") |
| add_subdirectory ("./swModel") |
| add_subdirectory ("./mapModel") |
| add_subdirectory ("./eventModel") |
| add_subdirectory ("./stimuliModel") |
| add_subdirectory ("./osModel") |
| |
| «IF Integer.parseInt(super.getProperty("CmakeUnityBuildTUPerCore","0")) > 0» |
| # set the unity-batch-size to a Processorcount dependend value, for more efficient compiling |
| if(COMMAND calc_unity_batch_size) |
| calc_unity_batch_size(«getProperty(PropertyKeys.PROJECT_NAME)» «super.getProperty("CmakeUnityBuildTUPerCore")») |
| endif() |
| «ENDIF» |
| |
| # use /bigobj to ensure big models to build with MSVC (e.g. FMTV WATERS Challenge 2017) |
| if(MSVC) |
| add_definitions(/bigobj) |
| message("Set /bigobj for big object files in MSVC") |
| endif() |
| if(MINGW) |
| set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wa,-mbig-obj") |
| message("Set -Wa,-mbig-obj for big object files in MINGW") |
| endif() |
| |
| add_custom_target(model_execution |
| COMMAND «getProperty(PropertyKeys.PROJECT_NAME)» |
| WORKING_DIRECTORY "${MODEL_WORKING_DIR}" |
| ) |
| |
| install( |
| TARGETS «getProperty(PropertyKeys.PROJECT_NAME)» |
| RUNTIME DESTINATION ${PROJECT_SOURCE_DIR}/bin/examples/ |
| ) |
| ''' |
| |
| def String getBuildScript() ''' |
| @ECHO OFF |
| |
| SET doClean=0 |
| SET cleanCmd=cmake --build ./build --config Debug --target clean |
| |
| SET doConfigure=0 |
| SET configCmd=cmake --no-warn-unused-cli -DCMAKE_EXPORT_COMPILE_COMMANDS:BOOL=TRUE -H./ -B./build |
| |
| SET doBuild=0 |
| SET buildCmd=cmake --build ./build --config Debug --target install |
| |
| IF "%1"=="" (GOTO :help) |
| |
| ::Loop through command line arguments |
| :loop |
| IF NOT "%1"=="" ( |
| If "%1"=="--clean" (SET doClean=1) |
| If "%1"=="--configure" (SET doConfigure=1) |
| If "%1"=="--build" (SET doBuild=1) |
| If "%1"=="--help" (GOTO :help) |
| SHIFT |
| GOTO :loop |
| ) |
| |
| :: cleaning |
| IF "%doClean%"=="1" ( |
| ECHO. |
| ECHO [proc] Build clean: Removing subdirectories \build, \lib, and \bin |
| IF EXIST .\build ( |
| DEL /f /S /Q .\build 1>nul |
| RD /S /Q .\build |
| ) |
| IF EXIST .\lib ( |
| DEL /f /S /Q .\lib 1>nul |
| RD /S /Q .\lib |
| ) |
| IF EXIST .\bin ( |
| DEL /f /S /Q .\bin 1>nul |
| RD /S /Q .\bin |
| ) |
| ) |
| |
| :: configuring |
| IF "%doConfigure%"=="1" ( |
| ECHO. |
| ECHO [proc] Configure all: %configCmd% |
| %configCmd% ) |
| |
| |
| :: building |
| IF "%doBuild%"=="1" ( |
| ECHO. |
| ECHO [proc] Build all: %buildCmd% |
| %buildCmd% ) |
| |
| GOTO :EOF |
| |
| :help |
| ECHO. |
| ECHO Usage: build [--clean] [--configure] [--build] [--help] |
| ECHO. |
| ECHO At least one command option must be passed. |
| ECHO. |
| ECHO Options: |
| ECHO --clean performs build clean |
| ECHO --configure configures the simulation project |
| ECHO --build builds the simulation project |
| ECHO --help shows command help |
| ECHO. |
| |
| :EOF |
| ''' |
| |
| def String getRunScript() ''' |
| «val executableName = super.getProperty(PropertyKeys.PROJECT_NAME)» |
| |
| @ECHO OFF |
| |
| SET runCmd=.\build\«getModulePath()»\Debug\«executableName».exe -t «getSimulationDuration()» -r «getTraceFormats()» -o «getTracePath()» |
| ECHO [proc] Run simulation: %runCmd% |
| %runCmd% |
| ''' |
| |
| private def String getTraceFormats() { |
| var result = ""; |
| var useDefault = true; |
| for (TraceType type : TraceType.values()) { |
| if (Boolean.parseBoolean(super.getProperty(type.toString(), "false"))) { |
| result += (useDefault ? "" : " ") + type.getName |
| useDefault = false; |
| } |
| } |
| return useDefault ? TraceType.BTF.getName : result; |
| } |
| |
| private def String getSimulationDuration() { |
| return super.getProperty(PropertyKeys.SIM_DURATION_IN_MS, "1000") |
| } |
| |
| private def String getTracePath() { |
| return Paths.get(super.getProperty(PropertyKeys.TRACE_FOLDER, "./traces")).toString |
| } |
| |
| } |