Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 45 additions & 21 deletions tools/projmgr/src/ProjMgrMlops.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#include "ProjMgrParser.h"
#include "ProjMgrUtils.h"

#include <algorithm>

using namespace std;

ProjMgrMlops::ProjMgrMlops(ProjMgrWorker* worker) : m_worker(worker) {
Expand Down Expand Up @@ -56,31 +58,34 @@ bool ProjMgrMlops::GetTargetSetItemRef(const TargetType& targetType, optional<co

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;
for (size_t index = 0; index < csolution.targetTypes.size(); index++) {
// default simulator: search target-types in reverse order, default hardware: in declaration order
const size_t candidate = simulatorDefault ? csolution.targetTypes.size() - index - 1 : index;
const auto& [name, type] = csolution.targetTypes[candidate];
if (GetTargetSetItemRef(type, nullopt, simulatorDefault, targetSetItem)) {
typeName = name;
return true;
}
}
return true;
}

TargetType targetType;
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);

const auto targetSetName = 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;
}
// 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;
}
Expand Down Expand Up @@ -263,6 +268,25 @@ 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 (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 (find_if(npuInfoItems.begin(), npuInfoItems.end(), matchesMacs) == npuInfoItems.end()) {
ProjMgrLogger::Get().Warn("mlops.npu.macs value does not match DFP device information", "", csolution.path);
}
}

// vela options
mlops.vela.options = BuildVelaOptions(mlops.npu, solutionMlops.vela);

Expand Down
11 changes: 11 additions & 0 deletions tools/projmgr/src/ProjMgrWorker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4353,6 +4353,14 @@ bool ProjMgrWorker::ListNpus(vector<string>& npus, const string& filter) {
if (!LoadPacks(context)) {
return false;
}
if (!selectedContext.empty()) {
if (!ProcessPrecedences(context, BoardOrDevice::Both)) {
return false;
}
if (!SetTargetAttributes(context, context.targetAttributes)) {
return false;
}
}
CollectNpuInfo(context);
for (const auto& npuInfoItem : context.npuInfoItems) {
string npuString = npuInfoItem.type + " (" + npuInfoItem.macs + "):";
Expand Down Expand Up @@ -6460,6 +6468,9 @@ void ProjMgrWorker::CollectNpuInfo(ContextItem& context) {
}
// collect NPU features for each processor
for (const auto& [pname, processor] : device->GetProcessors()) {
if (!context.deviceItem.pname.empty() && context.deviceItem.pname != pname) {
continue;
}
const auto& features = device->GetEffectiveProperties("feature", pname);
for (const auto& feature : features) {
if (feature->GetAttribute("type") != "NPU") {
Expand Down
33 changes: 33 additions & 0 deletions tools/projmgr/test/data/MLOps/npu_mismatch.csolution.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/Open-CMSIS-Pack/devtools/main/tools/projmgr/schemas/csolution.schema.json

solution:
compiler: AC6

mlops:
description: explicit npu configuration does not match DFP device information
npu:
type: Ethos-U85
macs: 128

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: core0
- project-context: core1

projects:
- project: core0/core0.cproject.yml
- project: core1/core1.cproject.yml
28 changes: 28 additions & 0 deletions tools/projmgr/test/src/ProjMgrUnitTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,21 @@ Ethos-U55 \\(128MACs\\):\n\
EXPECT_TRUE(regex_search(outStr, regex("\
Ethos-U55 \\(128MACs\\):\n\
ARM::RteTest_ARMCM0_Single, \\[cm0_core1\\] Cortex-M0\n")));

// verify NPU listing is restricted to the device and processor selected by a context
streamRedirect.ClearStringStreams();
const string& csolution = testinput_folder + "/MLOps/minimal.csolution.yml";
argv[3] = (char*)"--solution";
argv[4] = (char*)csolution.c_str();
argv[5] = (char*)"-c";
argv[6] = (char*)"core0+Hardware";
EXPECT_EQ(0, RunProjMgr(7, argv, 0));
outStr = streamRedirect.GetOutString();
EXPECT_NE(string::npos, outStr.find("ARM::RteTest_ARMCM0_Dual"));
EXPECT_NE(string::npos, outStr.find("[cm0_core0]"));
EXPECT_EQ(string::npos, outStr.find("ARM::RteTest_ARMCM0_Single"));
EXPECT_EQ(string::npos, outStr.find("[cm0_core1]"));
EXPECT_EQ(string::npos, outStr.find("Ethos-U55 (256MACs)"));
}

TEST_F(ProjMgrUnitTests, RunProjMgr_ListComponents) {
Expand Down Expand Up @@ -7869,6 +7884,7 @@ TEST_F(ProjMgrUnitTests, ParseCommandLine_MutualExclusionOptions) {
}

TEST_F(ProjMgrUnitTests, GenerateMLOps) {
StdStreamRedirect streamRedirect;
char* argv[5];
string csolution = testinput_folder + "/MLOps/minimal.csolution.yml";
argv[1] = (char*)"convert";
Expand Down Expand Up @@ -7908,6 +7924,18 @@ TEST_F(ProjMgrUnitTests, GenerateMLOps) {
EXPECT_EQ("Ethos-U55", mlopsMacsOnly["cbuild-mlops"]["npu"]["type"].as<string>());
EXPECT_EQ("128", mlopsMacsOnly["cbuild-mlops"]["npu"]["macs"].as<string>());

// Ethos-U85 and 128 MACs both exist in the DFP, but not as the same NPU capability
// verify that the type matches while the incompatible MAC count produces a warning
streamRedirect.ClearStringStreams();
csolution = testinput_folder + "/MLOps/npu_mismatch.csolution.yml";
argv[2] = (char*)csolution.c_str();
EXPECT_EQ(0, RunProjMgr(5, argv, m_envp));
const string warningStr = streamRedirect.GetErrorString();
EXPECT_EQ(string::npos, warningStr.find("npu_mismatch.csolution.yml - warning csolution: "
"mlops.npu.type value does not match DFP device information"));
EXPECT_NE(string::npos, warningStr.find("npu_mismatch.csolution.yml - warning csolution: "
"mlops.npu.macs value does not match DFP device information"));

// hardware target is not present
csolution = testinput_folder + "/MLOps/no_hardware.csolution.yml";
argv[2] = (char*)csolution.c_str();
Expand Down
Loading