diff --git a/tools/projmgr/include/ProjMgrMlops.h b/tools/projmgr/include/ProjMgrMlops.h index 23d3261dd..4fd263be2 100644 --- a/tools/projmgr/include/ProjMgrMlops.h +++ b/tools/projmgr/include/ProjMgrMlops.h @@ -9,6 +9,8 @@ #include "ProjMgrWorker.h" +#include + struct CsolutionItem; struct ContextItem; struct CustomItem; @@ -126,8 +128,10 @@ class ProjMgrMlops { private: ProjMgrWorker* m_worker = nullptr; bool FindTargetType(const CsolutionItem& csolution, const std::string& typeName, TargetType& targetType) const; - bool GetTargetSetItemRef(const TargetType& targetType, const std::string& targetTypeName, - const std::string& targetSetName, bool simulatorDefault, TargetSetItem& targetSet) const; + bool ResolveTargetSet(const CsolutionItem& csolution, const MlopsTargetItem target, + bool simulatorDefault, std::string& typeName, TargetSetItem& targetSetItem) const; + bool GetTargetSetItemRef(const TargetType& targetType, std::optional targetSetName, + bool simulatorDefault, TargetSetItem& targetSet) const; std::string BuildActive(const std::string& targetType, const std::string& targetSet) const; std::string GetCustomScalar(const CustomItem& custom, const std::string& key) const; std::string BuildVelaOptions(const MlopsNpuType& npu, const MlopsVelaItem& vela) const; diff --git a/tools/projmgr/src/ProjMgrMlops.cpp b/tools/projmgr/src/ProjMgrMlops.cpp index aa13f4538..d4857b73a 100644 --- a/tools/projmgr/src/ProjMgrMlops.cpp +++ b/tools/projmgr/src/ProjMgrMlops.cpp @@ -27,40 +27,64 @@ bool ProjMgrMlops::FindTargetType(const CsolutionItem& csolution, const string& return true; } } - ProjMgrLogger::Get().Error("mlops: target-type '" + typeName + "' not found"); return false; } -bool ProjMgrMlops::GetTargetSetItemRef(const TargetType& targetType, const string& targetTypeName, - const string& targetSetName, bool simulatorDefault, TargetSetItem& targetSet) const { - if (targetSetName.empty()) { - if (simulatorDefault) { - for (const auto& set : targetType.targetSet) { - if (set.debugger.name == "Arm-FVP") { - targetSet = set; - return true; - } +bool ProjMgrMlops::GetTargetSetItemRef(const TargetType& targetType, optional targetSetName, + bool simulator, TargetSetItem& targetSet) const { + if (!targetSetName.has_value()) { + for (const auto& set : targetType.targetSet) { + if ((simulator && set.debugger.name == "Arm-FVP") || + (!simulator && set.debugger.name != "Arm-FVP")) { + // default simulator or hardware target set + targetSet = set; + return true; } - ProjMgrLogger::Get().Error("mlops: simulator target with debugger 'Arm-FVP' not found"); - return false; - } - if (!targetType.targetSet.empty()) { - targetSet = targetType.targetSet.front(); - return true; } } else { for (const auto& set : targetType.targetSet) { - if (set.set == targetSetName) { + if (set.set == targetSetName.value()) { + // specified target set targetSet = set; return true; } } } - ProjMgrLogger::Get().Error("mlops: target-type '" + targetTypeName + "' target-set '" + - (targetSetName.empty() ? "" : targetSetName) + "' not found"); + // target set could not be determined return false; } +bool ProjMgrMlops::ResolveTargetSet(const CsolutionItem& csolution, const MlopsTargetItem target, + bool simulatorDefault, string& typeName, TargetSetItem& targetSetItem) const { + TargetType targetType; + if (target.targetType.empty()) { + if (!csolution.targetTypes.empty()) { + // default simulator: last target-type, default hardware: first target-type + typeName = simulatorDefault ? csolution.targetTypes.back().first : csolution.targetTypes.front().first; + targetType = simulatorDefault ? csolution.targetTypes.back().second : csolution.targetTypes.front().second; + } + } else { + typeName = target.targetType; + if (!FindTargetType(csolution, target.targetType, targetType)) { + // print error if specified target type was not found + ProjMgrLogger::Get().Error("mlops: target type '" + target.targetType + "' not found"); + return false; + } + } + // target set name = nullopt if target type is not specified, to differentiate from default (unnamed) target set + auto targetSetName = target.targetType.empty() ? nullopt : make_optional(target.targetSet); + if (!GetTargetSetItemRef(targetType, targetSetName, simulatorDefault, targetSetItem)) { + if (targetSetName.has_value()) { + // print error if specified target set was not found + ProjMgrLogger::Get().Error("mlops: " + (target.targetSet.empty() ? + ("no default unnamed target set for '" + target.targetType + "'") : + ("target set '" + target.targetType + '@' + target.targetSet + "' not found"))); + return false; + } + } + return true; +} + string ProjMgrMlops::BuildActive(const string& targetType, const string& targetSet) const { return targetSet.empty() ? targetType : targetType + "@" + targetSet; } @@ -110,31 +134,21 @@ bool ProjMgrMlops::CollectSettings(const CsolutionItem& csolution, MlopsType& ml const auto& solutionMlops = csolution.mlops; - // hardware and simulator target types - const string hardwareType = !solutionMlops.hardware.targetType.empty() ? solutionMlops.hardware.targetType : - (!csolution.targetTypes.empty() ? csolution.targetTypes.front().first : RteUtils::EMPTY_STRING); - const string simulatorType = !solutionMlops.simulator.targetType.empty() ? solutionMlops.simulator.targetType : - (!csolution.targetTypes.empty() ? csolution.targetTypes.back().first : RteUtils::EMPTY_STRING); - - // get hardware set - TargetType hardwareTargetType; - TargetSetItem hardwareTargetSet; - if (!FindTargetType(csolution, hardwareType, hardwareTargetType) || - !GetTargetSetItemRef(hardwareTargetType, hardwareType, solutionMlops.hardware.targetSet, false, hardwareTargetSet)) { - return false; - } - - // get simulator set - TargetType simulatorTargetType; - TargetSetItem simulatorTargetSet; - if (!FindTargetType(csolution, simulatorType, simulatorTargetType) || - !GetTargetSetItemRef(simulatorTargetType, simulatorType, solutionMlops.simulator.targetSet, true, simulatorTargetSet)) { + // resolve hardware and simulator targets and get references for their items + string hardwareType, simulatorType; + TargetSetItem hardwareTargetSet, simulatorTargetSet; + if (!ResolveTargetSet(csolution, solutionMlops.hardware, false, hardwareType, hardwareTargetSet) || + !ResolveTargetSet(csolution, solutionMlops.simulator, true, simulatorType, simulatorTargetSet)) { + // throw error if target is explicit specified but target set cannot be determined return false; } // get all context items map* contexts = nullptr; m_worker->GetContexts(contexts); + if (contexts->empty()) { + return false; + } // find hardware and simulator contexts vector hardwareContexts, simulatorContexts; @@ -142,9 +156,9 @@ bool ProjMgrMlops::CollectSettings(const CsolutionItem& csolution, MlopsType& ml vector&>> refs = { {hardwareTargetSet, hardwareType, hardwareContexts}, {simulatorTargetSet, simulatorType, simulatorContexts}}; for (auto& [targetSet, targetType, ref] : refs) { - for (const auto& image : targetSet.images) { - if (!image.context.empty()) { - const string contextName = image.context + "+" + targetType; + for (const auto& entry : targetSet.images) { + if (!entry.context.empty()) { + const string contextName = entry.context + "+" + targetType; if (contexts->find(contextName) != contexts->end()) { // process context precedences if needed auto& context = contexts->at(contextName); @@ -164,27 +178,35 @@ bool ProjMgrMlops::CollectSettings(const CsolutionItem& csolution, MlopsType& ml } // check if hardware and simulator contexts were found - vector&, const string&, const string&>> contextRefs = { - {hardwareContexts, hardwareType, hardwareTargetSet.set}, - {simulatorContexts, simulatorType, simulatorTargetSet.set} + vector&, const MlopsTargetItem&>> contextRefs = { + {hardwareContexts, solutionMlops.hardware}, + {simulatorContexts, solutionMlops.simulator} }; - for (const auto& [ref, targetType, targetSet] : contextRefs) { - if (ref.empty()) { - ProjMgrLogger::Get().Error("mlops: target-type '" + targetType + "' target-set '" + - (targetSet.empty() ? "" : targetSet) + "' project-contexts not found"); + for (const auto& [ref, t] : contextRefs) { + if (!t.targetType.empty() && ref.empty()) { + // print error if context for specified target type was not found + ProjMgrLogger::Get().Error("mlops: no project-context specified for target '" + + t.targetType + (t.targetSet.empty() ? "" : '@' + t.targetSet) + "'"); return false; } } - auto& hardwareContext = hardwareContexts.front(); - auto& simulatorContext = simulatorContexts.front(); + // get hardware and simulator context references + bool hardwareFound = !hardwareContexts.empty(); + bool simulatorFound = !simulatorContexts.empty(); + ContextItem emptyContext; + ContextItem& hardwareContext = hardwareFound ? hardwareContexts.front() : emptyContext; + ContextItem& simulatorContext = simulatorFound ? simulatorContexts.front() : emptyContext; + + // if hardware is not determined use first default context for processor type and access sequences + ContextItem& context = hardwareFound ? hardwareContext : contexts->begin()->second; // mlops description mlops.description = solutionMlops.description; // get hardware processor type ("Dcore") - if (hardwareContext.targetAttributes.find("Dcore") != hardwareContext.targetAttributes.end()) { - mlops.processor.type = hardwareContext.targetAttributes.at("Dcore"); + if (context.targetAttributes.find("Dcore") != context.targetAttributes.end()) { + mlops.processor.type = context.targetAttributes.at("Dcore"); } // npu type and macs @@ -244,40 +266,47 @@ bool ProjMgrMlops::CollectSettings(const CsolutionItem& csolution, MlopsType& ml } else { // explicit vela ini mlops.vela.ini = solutionMlops.vela.ini; - if (!m_worker->ProcessSequenceRelative(hardwareContext, mlops.vela.ini, csolution.directory, false)) { + if (!m_worker->ProcessSequenceRelative(context, mlops.vela.ini, csolution.directory, false)) { return false; } if (RteFsUtils::IsRelative(mlops.vela.ini)) { - RteFsUtils::NormalizePath(mlops.vela.ini, hardwareContext.directories.cprj); + RteFsUtils::NormalizePath(mlops.vela.ini, context.directories.cprj); } } - + // model name and clayer if (!solutionMlops.model.clayer.empty()) { mlops.model.name = solutionMlops.model.name.empty() ? "Algorithm" : solutionMlops.model.name; mlops.model.clayer = solutionMlops.model.clayer; - if (!m_worker->ProcessSequenceRelative(hardwareContext, mlops.model.clayer, csolution.directory, false)) { + if (!m_worker->ProcessSequenceRelative(context, mlops.model.clayer, csolution.directory, false)) { return false; } if (RteFsUtils::IsRelative(mlops.model.clayer)) { - RteFsUtils::NormalizePath(mlops.model.clayer, hardwareContext.directories.cprj); + RteFsUtils::NormalizePath(mlops.model.clayer, context.directories.cprj); } } - // set hardware and simulator run types - const string outBaseDir = hardwareContext.directories.cprj + "/" + hardwareContext.directories.outBaseDir; - SetMlopsRunType(mlops.hardware, hardwareType, hardwareTargetSet.set, hardwareContexts, outBaseDir, csolution.name); - SetMlopsRunType(mlops.simulator, simulatorType, simulatorTargetSet.set, simulatorContexts, outBaseDir, csolution.name); + if (hardwareFound) { + // set hardware run types + const string outBaseDir = hardwareContext.directories.cprj + "/" + hardwareContext.directories.outBaseDir; + SetMlopsRunType(mlops.hardware, hardwareType, hardwareTargetSet.set, hardwareContexts, outBaseDir, csolution.name); + } - // get debugger model and config-file - mlops.simulator.model = GetCustomScalar(simulatorTargetSet.debugger.custom, "model"); - mlops.simulator.configFile = GetCustomScalar(simulatorTargetSet.debugger.custom, "config-file"); - if (!mlops.simulator.configFile.empty()) { - if (!m_worker->ProcessSequenceRelative(simulatorContext, mlops.simulator.configFile, csolution.directory, false)) { - return false; - } - if (RteFsUtils::IsRelative(mlops.simulator.configFile)) { - RteFsUtils::NormalizePath(mlops.simulator.configFile, simulatorContext.directories.cprj); + if (simulatorFound) { + // set simulator run types + const string outBaseDir = simulatorContext.directories.cprj + "/" + simulatorContext.directories.outBaseDir; + SetMlopsRunType(mlops.simulator, simulatorType, simulatorTargetSet.set, simulatorContexts, outBaseDir, csolution.name); + + // get debugger model and config-file + mlops.simulator.model = GetCustomScalar(simulatorTargetSet.debugger.custom, "model"); + mlops.simulator.configFile = GetCustomScalar(simulatorTargetSet.debugger.custom, "config-file"); + if (!mlops.simulator.configFile.empty()) { + if (!m_worker->ProcessSequenceRelative(simulatorContext, mlops.simulator.configFile, csolution.directory, false)) { + return false; + } + if (RteFsUtils::IsRelative(mlops.simulator.configFile)) { + RteFsUtils::NormalizePath(mlops.simulator.configFile, simulatorContext.directories.cprj); + } } } diff --git a/tools/projmgr/test/data/MLOps/failure2.csolution.yml b/tools/projmgr/test/data/MLOps/failure2.csolution.yml index 7a8665f63..d2b3cad92 100644 --- a/tools/projmgr/test/data/MLOps/failure2.csolution.yml +++ b/tools/projmgr/test/data/MLOps/failure2.csolution.yml @@ -5,7 +5,7 @@ solution: mlops: simulator: - target: OtherSimulator + target: Simulator@OtherSet target-types: - type: Hardware diff --git a/tools/projmgr/test/data/MLOps/failure3.csolution.yml b/tools/projmgr/test/data/MLOps/failure3.csolution.yml index 0fd62d7b0..b9994352d 100644 --- a/tools/projmgr/test/data/MLOps/failure3.csolution.yml +++ b/tools/projmgr/test/data/MLOps/failure3.csolution.yml @@ -4,16 +4,26 @@ solution: compiler: AC6 mlops: + simulator: + target: Simulator@FVP-Test target-types: - type: Hardware device: RteTest_ARMCM0_Dual target-set: - set: + images: + - project-context: core0 + - project-context: core1 + - type: Simulator device: RteTest_ARMCM0_Dual target-set: - set: FVP-Test + debugger: + name: Arm-FVP + images: + - project-context: wrong_context projects: - project: core0/core0.cproject.yml diff --git a/tools/projmgr/test/data/MLOps/minimal.csolution.yml b/tools/projmgr/test/data/MLOps/minimal.csolution.yml index 1540861bb..dff4b2f8c 100644 --- a/tools/projmgr/test/data/MLOps/minimal.csolution.yml +++ b/tools/projmgr/test/data/MLOps/minimal.csolution.yml @@ -21,8 +21,6 @@ solution: - type: Simulator device: RteTest_ARMCM0_Dual - define: - - SIMULATOR variables: - AI-Layer: $SolutionDir()$/ai_layer/ai_layer.clayer.yml target-set: diff --git a/tools/projmgr/test/data/MLOps/no_hardware.csolution.yml b/tools/projmgr/test/data/MLOps/no_hardware.csolution.yml new file mode 100644 index 000000000..9bcb2f811 --- /dev/null +++ b/tools/projmgr/test/data/MLOps/no_hardware.csolution.yml @@ -0,0 +1,28 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/Open-CMSIS-Pack/devtools/main/tools/projmgr/schemas/csolution.schema.json + +solution: + compiler: AC6 + + mlops: + description: hardware target not present + model: + clayer: $AI-Layer$ + + target-types: + - type: Simulator + device: RteTest_ARMCM0_Dual + variables: + - AI-Layer: $SolutionDir()$/ai_layer/ai_layer.clayer.yml + target-set: + - set: FVP-Test + debugger: + name: Arm-FVP + model: FVP_Corstone_SSE-320 + config-file: fvp/fvp_config.txt + images: + - project-context: core0 + - project-context: core1 + + projects: + - project: core0/core0.cproject.yml + - project: core1/core1.cproject.yml diff --git a/tools/projmgr/test/data/MLOps/failure4.csolution.yml b/tools/projmgr/test/data/MLOps/no_simulator.csolution.yml similarity index 69% rename from tools/projmgr/test/data/MLOps/failure4.csolution.yml rename to tools/projmgr/test/data/MLOps/no_simulator.csolution.yml index bda135282..dd8741f46 100644 --- a/tools/projmgr/test/data/MLOps/failure4.csolution.yml +++ b/tools/projmgr/test/data/MLOps/no_simulator.csolution.yml @@ -4,25 +4,21 @@ solution: compiler: AC6 mlops: + description: simulator target not present + model: + clayer: $AI-Layer$ target-types: - type: Hardware device: RteTest_ARMCM0_Dual + variables: + - AI-Layer: $SolutionDir()$/ai_layer/ai_layer.clayer.yml target-set: - set: images: - project-context: core0 - project-context: core1 - - type: Simulator - device: RteTest_ARMCM0_Dual - target-set: - - set: FVP-Test - debugger: - name: Arm-FVP - images: - - project-context: wrong_context - projects: - project: core0/core0.cproject.yml - project: core1/core1.cproject.yml diff --git a/tools/projmgr/test/src/ProjMgrUnitTests.cpp b/tools/projmgr/test/src/ProjMgrUnitTests.cpp index e68e8fa2f..564303b8d 100644 --- a/tools/projmgr/test/src/ProjMgrUnitTests.cpp +++ b/tools/projmgr/test/src/ProjMgrUnitTests.cpp @@ -7749,12 +7749,27 @@ TEST_F(ProjMgrUnitTests, GenerateMLOps) { EXPECT_EQ("Ethos-U55", mlopsMacsOnly["cbuild-mlops"]["npu"]["type"].as()); EXPECT_EQ("128", mlopsMacsOnly["cbuild-mlops"]["npu"]["macs"].as()); + // hardware target is not present + csolution = testinput_folder + "/MLOps/no_hardware.csolution.yml"; + argv[2] = (char*)csolution.c_str(); + EXPECT_EQ(0, RunProjMgr(5, argv, m_envp)); + const YAML::Node& mlopsNoHardware = YAML::LoadFile(testinput_folder + "/MLOps/no_hardware.cbuild-mlops.yml"); + EXPECT_FALSE(mlopsNoHardware["cbuild-mlops"]["hardware"].IsDefined()); + EXPECT_TRUE(mlopsNoHardware["cbuild-mlops"]["simulator"].IsDefined()); + + // simulator target is not present + csolution = testinput_folder + "/MLOps/no_simulator.csolution.yml"; + argv[2] = (char*)csolution.c_str(); + EXPECT_EQ(0, RunProjMgr(5, argv, m_envp)); + const YAML::Node& mlopsNoSimulator = YAML::LoadFile(testinput_folder + "/MLOps/no_simulator.cbuild-mlops.yml"); + EXPECT_TRUE(mlopsNoSimulator["cbuild-mlops"]["hardware"].IsDefined()); + EXPECT_FALSE(mlopsNoSimulator["cbuild-mlops"]["simulator"].IsDefined()); + // test error cases: {csolution file, expected error message} const vector> failureCases = { - { "failure1", "mlops: target-type 'OtherHardware' not found" }, - { "failure2", "mlops: target-type 'Hardware' target-set '' not found" }, - { "failure3", "mlops: simulator target with debugger 'Arm-FVP' not found" }, - { "failure4", "mlops: target-type 'Simulator' target-set 'FVP-Test' project-contexts not found" }, + { "failure1", "mlops: target type 'OtherHardware' not found" }, + { "failure2", "mlops: target set 'Simulator@OtherSet' not found" }, + { "failure3", "mlops: no project-context specified for target 'Simulator@FVP-Test'" }, }; for (const auto& [name, expectedError] : failureCases) { csolution = testinput_folder + "/MLOps/" + name + ".csolution.yml"; @@ -7765,12 +7780,14 @@ TEST_F(ProjMgrUnitTests, GenerateMLOps) { } // file cannot be written - const string mlopsOutput = testinput_folder + "/MLOps/failure4.cbuild-mlops.yml"; - RteFsUtils::RemoveFile(mlopsOutput); + csolution = testinput_folder + "/MLOps/failure.csolution.yml"; + argv[2] = (char*)csolution.c_str(); + RteFsUtils::CopyCheckFile(testinput_folder + "/MLOps/minimal.csolution.yml", csolution, false); + const string mlopsOutput = testinput_folder + "/MLOps/failure.cbuild-mlops.yml"; EXPECT_TRUE(RteFsUtils::CreateDirectories(mlopsOutput)); EXPECT_EQ(1, RunProjMgr(5, argv, m_envp)); - const YAML::Node& cbuildIdx = YAML::LoadFile(testinput_folder + "/MLOps/failure4.cbuild-idx.yml"); - EXPECT_EQ("failure4.cbuild-mlops.yml - file cannot be written", - cbuildIdx["build-idx"]["cbuilds"][0]["messages"]["errors"][1].as()); + const YAML::Node& cbuildIdx = YAML::LoadFile(testinput_folder + "/MLOps/failure.cbuild-idx.yml"); + EXPECT_EQ("failure.cbuild-mlops.yml - file cannot be written", + cbuildIdx["build-idx"]["cbuilds"][0]["messages"]["errors"][0].as()); RteFsUtils::RemoveDir(mlopsOutput); }