diff --git a/tools/projmgr/include/ProjMgrMlops.h b/tools/projmgr/include/ProjMgrMlops.h index 4a0595515..5c64f7529 100644 --- a/tools/projmgr/include/ProjMgrMlops.h +++ b/tools/projmgr/include/ProjMgrMlops.h @@ -54,6 +54,7 @@ struct MlopsVelaType { struct MlopsModelType { std::string clayer; std::string name; + std::map additional; }; /** diff --git a/tools/projmgr/include/ProjMgrParser.h b/tools/projmgr/include/ProjMgrParser.h index 799fe1242..749c148df 100644 --- a/tools/projmgr/include/ProjMgrParser.h +++ b/tools/projmgr/include/ProjMgrParser.h @@ -602,6 +602,7 @@ struct MlopsVelaItem { struct MlopsModelItem { std::string clayer; std::string name; + std::map additional; }; /** diff --git a/tools/projmgr/schemas/common.schema.json b/tools/projmgr/schemas/common.schema.json index 27ae63f76..80e9b5b3b 100644 --- a/tools/projmgr/schemas/common.schema.json +++ b/tools/projmgr/schemas/common.schema.json @@ -211,7 +211,7 @@ "uniqueItems": true, "minItems": 1, "description": "List of variables. Can be used to define project components/layers.", - "items": {"type": "object", "additionalProperties": {"type": "string"}} + "items": {"type": "object", "additionalProperties": {"type": ["string", "number"]}} }, "VersionType": { "type": "string", @@ -2761,7 +2761,7 @@ "description": "NPU configuration.", "properties": { "type": { "type": "string", "description": "NPU type (default: first NPU from DFP device features)." }, - "macs": { "type": "number", "description": "Number of MACs (default: first NPU from DFP device features)." } + "macs": { "type": [ "string", "number" ], "description": "Number of MACs (default: first NPU from DFP device features)." } }, "additionalProperties": false }, @@ -2783,7 +2783,7 @@ "clayer": { "type": "string", "description": "Path to the AI clayer file or variable." }, "name": { "type": "string", "description": "Model name used as namespace (default: Algorithm)." } }, - "additionalProperties": false + "additionalProperties": { "type": [ "string", "number" ] } }, "hardware": { "$ref": "#/definitions/MlopsTargetType" }, "simulator": { "$ref": "#/definitions/MlopsTargetType" } @@ -2829,7 +2829,7 @@ "clayer": { "type": "string", "description": "Path to the AI clayer file." }, "name": { "type": "string", "description": "Model name used as namespace." } }, - "additionalProperties": false + "additionalProperties": { "type": [ "string", "number" ] } }, "hardware": { "$ref": "#/definitions/MlopsRunDescType" }, "simulator": { "$ref": "#/definitions/MlopsRunDescType" } diff --git a/tools/projmgr/src/ProjMgrCbuildMlops.cpp b/tools/projmgr/src/ProjMgrCbuildMlops.cpp index d2ed54301..2545f790b 100644 --- a/tools/projmgr/src/ProjMgrCbuildMlops.cpp +++ b/tools/projmgr/src/ProjMgrCbuildMlops.cpp @@ -64,6 +64,9 @@ void ProjMgrCbuildMlops::SetModelNode(YAML::Node node, const MlopsModelType& mod SetNodeValue(node[YAML_CLAYER], FormatPath(model.clayer, m_directory)); } SetNodeValue(node[YAML_NAME], model.name); + for (const auto& [key, value] : model.additional) { + SetNodeValue(node[key], value); + } } void ProjMgrCbuildMlops::SetRunNode(YAML::Node node, const MlopsRunType& run) { diff --git a/tools/projmgr/src/ProjMgrMlops.cpp b/tools/projmgr/src/ProjMgrMlops.cpp index 96094ec96..4555cc12e 100644 --- a/tools/projmgr/src/ProjMgrMlops.cpp +++ b/tools/projmgr/src/ProjMgrMlops.cpp @@ -226,6 +226,19 @@ bool ProjMgrMlops::CollectSettings(const CsolutionItem& csolution, MlopsType& ml // mlops description mlops.description = solutionMlops.description; + const auto expand = [&context](string& value) { + value = RteUtils::ExpandAccessSequences(value, context.variables); + // unresolved variables are empty in MLOps settings + size_t start = 0; + while ((start = value.find('$', start)) != string::npos) { + const size_t end = value.find('$', start + 1); + if (end == string::npos) { + break; + } + value.erase(start, end - start + 1); + } + }; + // get hardware processor type ("Dcore") if (context.targetAttributes.find("Dcore") != context.targetAttributes.end()) { mlops.processor.type = context.targetAttributes.at("Dcore"); @@ -234,6 +247,8 @@ bool ProjMgrMlops::CollectSettings(const CsolutionItem& csolution, MlopsType& ml // npu type and macs mlops.npu.type = solutionMlops.npu.type; mlops.npu.macs = solutionMlops.npu.macs; + expand(mlops.npu.type); + expand(mlops.npu.macs); // filter npu info items vector npuInfoItems; @@ -269,18 +284,18 @@ bool ProjMgrMlops::CollectSettings(const CsolutionItem& csolution, MlopsType& ml } // print warnings if the required NPU type and/or MACs do not match the DFP device information - if (!solutionMlops.npu.type.empty()) { - const auto matchesType = [&solutionMlops](const NpuInfoItem& npu) { - return npu.type == solutionMlops.npu.type; + if (!mlops.npu.type.empty()) { + const auto matchesType = [&mlops](const NpuInfoItem& npu) { + return npu.type == mlops.npu.type; }; if (find_if(npuInfoItems.begin(), npuInfoItems.end(), matchesType) == npuInfoItems.end()) { ProjMgrLogger::Get().Warn("mlops.npu.type value does not match DFP device information", "", csolution.path); } } - if (!solutionMlops.npu.macs.empty()) { - const auto matchesMacs = [&solutionMlops](const NpuInfoItem& npu) { - return (solutionMlops.npu.type.empty() || npu.type == solutionMlops.npu.type) && - RteUtils::StringToULL(npu.macs) == RteUtils::StringToULL(solutionMlops.npu.macs); + if (!mlops.npu.macs.empty()) { + const auto matchesMacs = [&mlops](const NpuInfoItem& npu) { + return (mlops.npu.type.empty() || npu.type == mlops.npu.type) && + RteUtils::StringToULL(npu.macs) == RteUtils::StringToULL(mlops.npu.macs); }; if (find_if(npuInfoItems.begin(), npuInfoItems.end(), matchesMacs) == npuInfoItems.end()) { ProjMgrLogger::Get().Warn("mlops.npu.macs value does not match DFP device information", "", csolution.path); @@ -288,7 +303,11 @@ bool ProjMgrMlops::CollectSettings(const CsolutionItem& csolution, MlopsType& ml } // vela options - mlops.vela.options = BuildVelaOptions(mlops.npu, solutionMlops.vela); + MlopsVelaItem vela = solutionMlops.vela; + expand(vela.system); + expand(vela.memory); + expand(vela.misc); + mlops.vela.options = BuildVelaOptions(mlops.npu, vela); // vela ini if (solutionMlops.vela.ini.empty()) { @@ -318,7 +337,9 @@ bool ProjMgrMlops::CollectSettings(const CsolutionItem& csolution, MlopsType& ml // model name and clayer if (!solutionMlops.model.clayer.empty()) { mlops.model.name = solutionMlops.model.name.empty() ? "Algorithm" : solutionMlops.model.name; + expand(mlops.model.name); mlops.model.clayer = solutionMlops.model.clayer; + expand(mlops.model.clayer); if (!m_worker->ProcessSequenceRelative(context, mlops.model.clayer, csolution.directory, false)) { return false; } @@ -326,6 +347,10 @@ bool ProjMgrMlops::CollectSettings(const CsolutionItem& csolution, MlopsType& ml RteFsUtils::NormalizePath(mlops.model.clayer, context.directories.cprj); } } + mlops.model.additional = solutionMlops.model.additional; + for (auto& [key, value] : mlops.model.additional) { + expand(value); + } if (hardwareFound) { // set hardware run types diff --git a/tools/projmgr/src/ProjMgrYamlParser.cpp b/tools/projmgr/src/ProjMgrYamlParser.cpp index 294ea6e99..0c77e004f 100644 --- a/tools/projmgr/src/ProjMgrYamlParser.cpp +++ b/tools/projmgr/src/ProjMgrYamlParser.cpp @@ -1159,7 +1159,7 @@ void ProjMgrYamlParser::ParseMlops(const YAML::Node& parent, const string& file, if (mlopsNode[YAML_NPU].IsDefined()) { const YAML::Node& npuNode = mlopsNode[YAML_NPU]; ParseString(npuNode, YAML_TYPE, mlops.npu.type); - ParseNumber(npuNode, file, YAML_MACS, mlops.npu.macs); + ParseString(npuNode, YAML_MACS, mlops.npu.macs); } if (mlopsNode[YAML_VELA].IsDefined()) { const YAML::Node& velaNode = mlopsNode[YAML_VELA]; @@ -1172,6 +1172,12 @@ void ProjMgrYamlParser::ParseMlops(const YAML::Node& parent, const string& file, const YAML::Node& modelNode = mlopsNode[YAML_MODEL]; ParsePortablePath(modelNode, file, YAML_CLAYER, mlops.model.clayer); ParseString(modelNode, YAML_NAME, mlops.model.name); + for (const auto& item : modelNode) { + const string key = item.first.as(); + if (key != YAML_CLAYER && key != YAML_NAME) { + mlops.model.additional[key] = item.second.as(); + } + } } if (mlopsNode[YAML_HARDWARE].IsDefined()) { const YAML::Node& hardwareNode = mlopsNode[YAML_HARDWARE]; diff --git a/tools/projmgr/test/data/MLOps/extended.csolution.yml b/tools/projmgr/test/data/MLOps/extended.csolution.yml index 14477d359..d8cf11eb9 100644 --- a/tools/projmgr/test/data/MLOps/extended.csolution.yml +++ b/tools/projmgr/test/data/MLOps/extended.csolution.yml @@ -9,16 +9,18 @@ solution: mlops: # enable *.cbuild-mlops.yml description: ML model with extended configuration npu: - type: Ethos-U85 # specify NPU (default: first NPU from DFP device features) - macs: 256 # specify MACs (default: first NPU from DFP device features) + type: $NPU-Type$ # specify NPU (default: first NPU from DFP device features) + macs: $NPU-Macs$ # specify MACs (default: first NPU from DFP device features) vela: ini: vela/custom.ini # explicit INI file (default: use INI file from DFP) - system: System_Config # system configuration from INI file - memory: Memory_Mode # memory configuration from INI file - misc: --extra-options # string with additional options for Vela + system: $Vela-System$ # system configuration from INI file + memory: $Vela-Memory$ # memory configuration from INI file + misc: $Vela-Misc$ # additional options for Vela model: clayer: $AI-Layer$ # path to layer or variable - name: Algorithm # optional model name (default Algorithm), serves as namespace + name: $Model-Name$ # optional model name (default Algorithm), serves as namespace + extra: $Model-Extra$ # additional model property + unresolved: $Undefined$ # undefined variables resolve to an empty value hardware: # hardware target for testing target: Hardware # explicit [@] (default: first target-type, first set) simulator: # simulator target for testing @@ -29,6 +31,13 @@ solution: device: RteTest_ARMCM0_Dual variables: - AI-Layer: $SolutionDir()$/ai_layer/ai_layer.clayer.yml + - NPU-Type: Ethos-U85 + - NPU-Macs: 256 + - Vela-System: System_Config + - Vela-Memory: Memory_Mode + - Vela-Misc: --extra-options + - Model-Name: ResolvedAlgorithm + - Model-Extra: extra-value target-set: - set: images: diff --git a/tools/projmgr/test/data/MLOps/ref/extended.cbuild-mlops.yml b/tools/projmgr/test/data/MLOps/ref/extended.cbuild-mlops.yml index 11d6e4eb4..7ae701865 100644 --- a/tools/projmgr/test/data/MLOps/ref/extended.cbuild-mlops.yml +++ b/tools/projmgr/test/data/MLOps/ref/extended.cbuild-mlops.yml @@ -11,7 +11,8 @@ cbuild-mlops: options: --accelerator-config ethos-u85-256 --system-config System_Config --memory-mode Memory_Mode --extra-options model: clayer: ai_layer/ai_layer.clayer.yml - name: Algorithm + name: ResolvedAlgorithm + extra: extra-value hardware: active: Hardware cbuild-run: out/extended+Hardware.cbuild-run.yml