From 9c5ab73faaf154debc4af93b6f9c249e79f59fd4 Mon Sep 17 00:00:00 2001 From: Evgueni Driouk Date: Wed, 18 Mar 2026 09:01:47 +0100 Subject: [PATCH] RTE Model: improve collecting device properties for multi-core devices --- libs/rtemodel/include/RteDevice.h | 10 +- libs/rtemodel/include/RteTarget.h | 6 +- libs/rtemodel/src/RteDevice.cpp | 47 ++-- libs/rtemodel/src/RteTarget.cpp | 8 +- libs/rtemodel/test/src/RteModelTest.cpp | 23 ++ libs/rtemodel/test/src/RteModelTestConfig.cpp | 23 ++ libs/rtemodel/test/src/RteModelTestConfig.h | 4 + .../image-only-multicore+CM0.cbuild-run.yml | 206 ++++++++++++++++++ tools/projmgr/test/src/ProjMgrUnitTests.cpp | 7 +- 9 files changed, 289 insertions(+), 45 deletions(-) create mode 100644 tools/projmgr/test/data/ImageOnly/ref/image-only-multicore+CM0.cbuild-run.yml diff --git a/libs/rtemodel/include/RteDevice.h b/libs/rtemodel/include/RteDevice.h index 8e256e0d5..b19909952 100644 --- a/libs/rtemodel/include/RteDevice.h +++ b/libs/rtemodel/include/RteDevice.h @@ -8,7 +8,7 @@ */ /******************************************************************************/ /* - * Copyright (c) 2020-2021 Arm Limited. All rights reserved. + * Copyright (c) 2020-2026 Arm Limited. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 */ @@ -1132,7 +1132,7 @@ class RteDeviceItem : public RteDeviceElement * @brief collect effective properties for a supplied tag (e.g. "debug") and processor (e.g. "core_one") * @param tag property tag * @param properties reference to list of pointers to RteDeviceProperty to fill - * @param pName processor name, empty to collect properties for all processors + * @param pName processor name; if empty, collects only common properties (without Pname) * @param bRecursive flag to collect properties recursively from RteDeviceItem parent chain */ void CollectEffectiveProperties(const std::string& tag, std::list& properties, const std::string& pName = EMPTY_STRING, bool bRecursive = true) const; @@ -1140,16 +1140,16 @@ class RteDeviceItem : public RteDeviceElement /** * @brief collect all effective properties for given processor name * @param properties reference to RteDevicePropertyMap to fill - * @param pName processor name, empty to collect properties for all processors + * @param pName processor name; if empty, collects only common properties (without Pname) */ void CollectEffectiveProperties(RteDevicePropertyMap& properties, const std::string& pName = EMPTY_STRING) const; /** * @brief fill m_effectiveProperties member by calling via CollectEffectiveProperties(RteDevicePropertyMap&, const std::string&) - * @param pName processor name, empty to collect properties for all processors + * @param pName processor name; if empty, collects only common properties (without Pname) */ - void CollectEffectiveProperties(const std::string& pName = EMPTY_STRING); + const RteDevicePropertyMap& CollectEffectiveProperties(const std::string& pName = EMPTY_STRING); protected: std::map m_processors; // processor properties diff --git a/libs/rtemodel/include/RteTarget.h b/libs/rtemodel/include/RteTarget.h index 4a8ec13e4..2e1f4db76 100644 --- a/libs/rtemodel/include/RteTarget.h +++ b/libs/rtemodel/include/RteTarget.h @@ -8,7 +8,7 @@ */ /******************************************************************************/ /* - * Copyright (c) 2020-2021 Arm Limited. All rights reserved. + * Copyright (c) 2020-2026 Arm Limited. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 */ @@ -897,7 +897,7 @@ class RteTarget : public RteItem /** * @brief getter for list of boards compatible with target's device - * @param boards collection of RteBoad to fill + * @param boards collection of RteBoard to fill */ void GetBoards(std::vector& boards) const; @@ -1042,7 +1042,7 @@ class RteTarget : public RteItem void CollectPreIncludeStrings(RteComponent* c, int count); - void AddBoadProperties(RteDeviceItem* device, const std::string& processorName); + void AddBoardProperties(RteDeviceItem* device, const std::string& processorName); void AddAlgorithm(RteItem* algo, RteItem* holder); std::string NormalizeIncPath(const std::string& path) const; diff --git a/libs/rtemodel/src/RteDevice.cpp b/libs/rtemodel/src/RteDevice.cpp index 607576c27..a993db961 100644 --- a/libs/rtemodel/src/RteDevice.cpp +++ b/libs/rtemodel/src/RteDevice.cpp @@ -6,7 +6,7 @@ */ /******************************************************************************/ /* - * Copyright (c) 2020-2021 Arm Limited. All rights reserved. + * Copyright (c) 2020-2026 Arm Limited. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 */ @@ -709,7 +709,7 @@ void RteDeviceItem::CollectEffectiveProperties(const string& tag, list(child); if (p) { const string& propPname = p->GetProcessorName(); - if (pName.empty() || propPname.empty() || propPname == pName) { + if (propPname.empty() || propPname == pName) { const string& id = p->GetID(); RteDeviceProperty* pInserted = RteDeviceProperty::GetPropertyFromList(id, properties); if (p == pInserted) @@ -736,8 +736,7 @@ void RteDeviceItem::CollectEffectiveProperties(const string& tag, listsecond.m_propertyMap; CollectEffectiveProperties(pmap, pName); - for (auto [_, l] : pmap) { + for (const auto& [_, l] : pmap) { for (auto p : l) { p->CalculateCachedValues(); } } + return pmap; } @@ -779,32 +779,21 @@ const list& RteEffectiveProperties::GetProperties(const stri const RteDevicePropertyMap& RteDeviceItem::GetEffectiveProperties(const string& pName) { - if (m_effectiveProperties.empty()) { - for (auto [pn, p] : m_processors) { - CollectEffectiveProperties(pn); + if(pName.empty() || contains_key(m_processors, pName)) { + auto itp = m_effectiveProperties.find(pName); + if(itp != m_effectiveProperties.end()) { + return itp->second.m_propertyMap; } + return CollectEffectiveProperties(pName); } - - auto itp = m_effectiveProperties.find(pName); - if (itp != m_effectiveProperties.end()) { - return itp->second.m_propertyMap; - } - static const RteDevicePropertyMap EMPTY_PROPERTY_MAP; return EMPTY_PROPERTY_MAP; } const list& RteDeviceItem::GetEffectiveProperties(const string& tag, const string& pName) { - if (m_effectiveProperties.empty()) { - GetEffectiveProperties(pName); - } - auto itp = m_effectiveProperties.find(pName); - if (itp != m_effectiveProperties.end()) { - const RteEffectiveProperties& effectiveProps = itp->second; - return effectiveProps.GetProperties(tag); - } - return EMPTY_PROPERTY_LIST; + const RteDevicePropertyMap& effectivePropMap = GetEffectiveProperties(pName); + return get_or_default_const_ref(effectivePropMap, tag, EMPTY_PROPERTY_LIST); } RteDeviceProperty* RteDeviceItem::GetSingleEffectiveProperty(const string& tag, const string& pName) @@ -817,11 +806,11 @@ RteDeviceProperty* RteDeviceItem::GetSingleEffectiveProperty(const string& tag, std::list RteDeviceItem::GetAllEffectiveProperties(const std::string& tag) { - // Make a copy, simpler to merge with potential processor specific memories - list properties = GetEffectiveProperties(tag, RteUtils::EMPTY_STRING); + // Collect a list of unique properties (by pointer) across all processors for the given tag, without merging/normalizing them + list properties; // Iterate over processors - for(auto [pname, _] : GetProcessors()) { - // Collect processor - unique memories + for (const auto& [pname, _] : GetProcessors()) { + // Collect processor-specific properties for this tag const list& procProps = GetEffectiveProperties(tag, pname); for(auto p : procProps) { if(std::find(properties.begin(), properties.end(), p) == properties.end()) { @@ -1226,7 +1215,7 @@ string RteDeviceItemAggregate::GetSummaryString() const // Memory (RAM/ROM) unsigned long long ramSize = 0, romSize = 0; // Get all memory properties - list mems = item->GetAllEffectiveProperties("memory"); + const list& mems = item->GetAllEffectiveProperties("memory"); for (auto memsIt = mems.begin(); memsIt != mems.end(); ++memsIt) { RteDeviceMemory* mem = dynamic_cast(*memsIt); if (!mem) { diff --git a/libs/rtemodel/src/RteTarget.cpp b/libs/rtemodel/src/RteTarget.cpp index c6570e604..c44aee9bc 100644 --- a/libs/rtemodel/src/RteTarget.cpp +++ b/libs/rtemodel/src/RteTarget.cpp @@ -211,7 +211,7 @@ void RteTarget::SetBoard(RteBoard* board) { if (project) project->SetBoardInfo(GetName(), board); - AddBoadProperties(GetDevice(), GetProcessorName()); + AddBoardProperties(GetDevice(), GetProcessorName()); } @@ -622,7 +622,7 @@ void RteTarget::ProcessAttributes() // called from SetAttributes(), AddAttribute AddDeviceProperties(m_device, GetProcessorName()); }; -void RteTarget::AddBoadProperties(RteDeviceItem* device, const string& processorName) { +void RteTarget::AddBoardProperties(RteDeviceItem* device, const string& processorName) { // remove all board algos if any: target can only refer to a single board for (auto it = m_algos.begin(); it != m_algos.end();) { @@ -658,14 +658,14 @@ void RteTarget::AddDeviceProperties(RteDeviceItem* d, const string& processorNam return; } - AddBoadProperties(d, processorName); + AddBoardProperties(d, processorName); string packagePath = RteUtils::ExtractFilePath(package->GetPackageFileName(), true); // get properties for given processor: const RteDevicePropertyMap& propMap = d->GetEffectiveProperties(processorName); for (auto itpm = propMap.begin(); itpm != propMap.end(); ++itpm) { - const string& propType = itpm->first; // processor, feature, mamory, etc. + const string& propType = itpm->first; // processor, feature, memory, etc. const list& props = itpm->second; for (auto itp = props.begin(); itp != props.end(); ++itp) { RteDeviceProperty *p = *itp; diff --git a/libs/rtemodel/test/src/RteModelTest.cpp b/libs/rtemodel/test/src/RteModelTest.cpp index 83f617623..db9c2f34f 100644 --- a/libs/rtemodel/test/src/RteModelTest.cpp +++ b/libs/rtemodel/test/src/RteModelTest.cpp @@ -179,6 +179,29 @@ TEST(RteModelTest, LoadPacks) { // test recommended memory attributes: name and access summary = da->GetSummaryString(); EXPECT_EQ(summary, "ARM Cortex-M4, 10 MHz, 128 KiB RAM, 256 KiB ROM"); + auto di = da->GetDeviceItem(); + EXPECT_EQ(di->GetProcessorCount(), 1); + EXPECT_TRUE(di->GetEffectiveProperties("foo").empty()); + const auto& m4Properties = di->GetEffectiveProperties(""); + EXPECT_FALSE(m4Properties.empty()); + + da = rteModel->GetDeviceAggregate("RteTest_ARMCM0_Dual", "ARM:82"); + ASSERT_NE(da, nullptr); + // test recommended memory attributes: name and access + summary = da->GetSummaryString(); + EXPECT_EQ(summary, "ARM Cortex-M0, 10 MHz, ARM Cortex-M0, 10 MHz, 256 KiB RAM, 768 KiB ROM"); + di = da->GetDeviceItem(); + ASSERT_NE(di, nullptr); + EXPECT_EQ(di->GetProcessorCount(), 2); + const auto& commonProperties = di->GetEffectiveProperties(""); + const auto& core0Properties = di->GetEffectiveProperties("cm0_core0"); + const auto& core1Properties = di->GetEffectiveProperties("cm0_core1"); + + EXPECT_TRUE(RteModelTestConfig::IsSubset(commonProperties, core0Properties)); + EXPECT_TRUE(RteModelTestConfig::IsSubset(commonProperties, core1Properties)); + EXPECT_FALSE(RteModelTestConfig::IsSubset(core0Properties, commonProperties)); + EXPECT_FALSE(RteModelTestConfig::IsSubset(core1Properties, commonProperties)); + EXPECT_FALSE(RteModelTestConfig::IsSubset(core0Properties, core1Properties)); RteBoard* board = rteModel->FindBoard("RteTest board listing (Rev.C)"); ASSERT_NE(board, nullptr); diff --git a/libs/rtemodel/test/src/RteModelTestConfig.cpp b/libs/rtemodel/test/src/RteModelTestConfig.cpp index 82939bdee..e95552ab4 100644 --- a/libs/rtemodel/test/src/RteModelTestConfig.cpp +++ b/libs/rtemodel/test/src/RteModelTestConfig.cpp @@ -9,6 +9,7 @@ #include #include +#include using namespace std; @@ -60,6 +61,28 @@ void RteModelTestConfig::TearDown() RteFsUtils::DeleteTree(localPacks); } + + +bool RteModelTestConfig::IsSubset( const RteDevicePropertyMap& subset, const RteDevicePropertyMap& superset) +{ + for(const auto& [key, subsetList] : subset) { + auto it = superset.find(key); + if(it == superset.end()) { + return false; + } + std::unordered_set supersetListAsSet(it->second.begin(), it->second.end()); + + for(auto* ptr : subsetList) { + if(supersetListAsSet.find(ptr) == supersetListAsSet.end()) { + return false; + } + } + } + return true; +} + + + void RteModelTestConfig::compareFile(const string& newFile, const string& refFile, const std::unordered_map& expectedChangedFlags, const string& toolchain) const { ifstream streamNewFile, streamRefFile; diff --git a/libs/rtemodel/test/src/RteModelTestConfig.h b/libs/rtemodel/test/src/RteModelTestConfig.h index 5285d2ade..231cfe384 100644 --- a/libs/rtemodel/test/src/RteModelTestConfig.h +++ b/libs/rtemodel/test/src/RteModelTestConfig.h @@ -18,6 +18,8 @@ #include #include +#include "RteDevice.h" + class RteModelTestConfig : public ::testing::Test { public: @@ -26,6 +28,8 @@ class RteModelTestConfig : public ::testing::Test void compareFile(const std::string& newFile, const std::string& refFile, const std::unordered_map& expectedChangedFlags, const std::string& toolchain) const; + static bool IsSubset(const RteDevicePropertyMap& subset, const RteDevicePropertyMap& superset); + protected: void SetUp() override; void TearDown() override; diff --git a/tools/projmgr/test/data/ImageOnly/ref/image-only-multicore+CM0.cbuild-run.yml b/tools/projmgr/test/data/ImageOnly/ref/image-only-multicore+CM0.cbuild-run.yml new file mode 100644 index 000000000..402f2a6d2 --- /dev/null +++ b/tools/projmgr/test/data/ImageOnly/ref/image-only-multicore+CM0.cbuild-run.yml @@ -0,0 +1,206 @@ +cbuild-run: + generated-by: csolution version 0.0.0+g738a931a + solution: ../image-only-multicore.csolution.yml + target-type: CM0 + target-set: + device: ARM::RteTest_ARMCM0_Dual + device-pack: ARM::RteTest_DFP@0.2.0 + output: + - file: ../images/image1.elf + type: elf + load: image+symbols + pname: cm0_core0 + - file: ../images/image2.elf + type: elf + load: image+symbols + pname: cm0_core1 + system-resources: + memory: + - name: FLASH_DUAL + access: rx + start: 0x00000000 + size: 0x00080000 + pname: cm0_core0 + from-pack: ARM::RteTest_DFP@0.2.0 + - name: SRAM_DUAL + access: rwx + start: 0x80000000 + size: 0x00020000 + pname: cm0_core1 + from-pack: ARM::RteTest_DFP@0.2.0 + - name: IROM1 + access: rx + start: 0x00000000 + size: 0x00040000 + from-pack: ARM::RteTest_DFP@0.2.0 + - name: IRAM1 + access: rwx + start: 0x20000000 + size: 0x00020000 + from-pack: ARM::RteTest_DFP@0.2.0 + processors: + - core: Cortex-M0 + revision: r0p0 + pname: cm0_core0 + endian: configurable + fpu: none + mpu: none + dsp: none + trustzone: none + mve: fp + pacbti: none + max-clock: 10000000 + cdecp: 0x12 + - core: Cortex-M0 + revision: r0p0 + pname: cm0_core1 + endian: configurable + fpu: dp + mpu: none + dsp: present + trustzone: present + mve: int + pacbti: present + max-clock: 10000000 + punits: 1 + cdecp: 0x34 + system-descriptions: + - file: ${CMSIS_PACK_ROOT}/ARM/RteTest_DFP/0.2.0/Device/ARM/SVD/ARMCM0.svd + type: svd + debugger: + name: CMSIS-DAP@pyOCD + protocol: swd + clock: 10000000 + dbgconf: ../.cmsis/image-only-multicore+CM0.dbgconf + gdbserver: + - port: 3333 + - port: 3334 + pname: cm0_core0 + - port: 3335 + pname: cm0_core1 + debug-vars: + vars: | + __var DbgMCU_CR = 0x00000007; // DBGMCU_CR: DBG_SLEEP, DBG_STOP, DBG_STANDBY + __var TraceClk_Pin = 0x00040002; // PE2 + __var TraceD0_Pin = 0x00040003; // PE3 + __var TraceD1_Pin = 0x00040004; // PE4 + debug-sequences: + - name: DebugDeviceUnlock + blocks: + - execute: | + Sequence("CheckID"); + - name: DebugCoreStart + blocks: + - execute: | + // Replication of Standard Functionality + Write32(0xE000EDF0, 0xA05F0001); // Enable Core Debug via DHCSR + - info: DbgMCU registers + execute: | + // Device Specific Debug Setup + Write32(0x40021018, Read32(0x40021018) | 0x00400000); // Set RCC_APB2ENR.DBGMCUEN + - name: CheckID + blocks: + - execute: | + __var pidr1 = 0; + __var pidr2 = 0; + __var jep106id = 0; + __var ROMTableBase = 0; + + __ap = 0; // AHB-AP + + ROMTableBase = ReadAP(0xF8) & ~0x3; + + pidr1 = Read32(ROMTableBase + 0x0FE4); + pidr2 = Read32(ROMTableBase + 0x0FE8); + jep106id = ((pidr2 & 0x7) << 4 ) | ((pidr1 >> 4) & 0xF); + - if: jep106id != 0x20 + execute: | + Query(0, "Incorrect ID! Abort connection", 1); + Message(2, "Incorrect ID! Abort connection."); + - while: (ReadDP(DP_CTRL_STAT) & 0xA0000000) != 0xA0000000 + timeout: 1000000 + - name: DebugPortStop + blocks: + - execute: | + __var connectionFlash = ( __connection & 0xF ) == 2 ; + __var FLASH_BASE = 0x40022000 ; + __var FLASH_CR = FLASH_BASE + 0x10 ; + __var OBL_LAUNCH_BIT = ( 1 << 13 ) ; + __var FLASH_CR_Value = 0 ; + __var DoDebugPortStop = 1 ; + __var DP_CTRL_STAT = 0x4 ; + __var DP_SELECT = 0x8 ; + - if: connectionFlash && DoDebugPortStop + execute: | + DoDebugPortStop = 0 ; + FLASH_CR_Value = Read32( FLASH_CR ) ; + __errorcontrol = 1 ; + // write OBL_LAUNCH bit (causes a reset) + Write32( FLASH_CR, FLASH_CR_Value | ( OBL_LAUNCH_BIT ) ) ; + __errorcontrol = 0 ; + - if: DoDebugPortStop + execute: | + // Switch to DP Register Bank 0 + WriteDP(DP_SELECT, 0x00000000); + // Power Down Debug port + WriteDP(DP_CTRL_STAT, 0x00000000); + programming: + - algorithm: ${CMSIS_PACK_ROOT}/ARM/RteTest_DFP/0.2.0/Device/ARM/Flash/CortexM4Dual.FLM + start: 0x000A0000 + size: 0x00020000 + ram-start: 0x000C0000 + ram-size: 0x00040000 + pname: cm0_core1 + - algorithm: ${CMSIS_PACK_ROOT}/ARM/RteTest_DFP/0.2.0/Device/ARM/Flash/FAMILY.FLM + start: 0x00000000 + size: 0x00040000 + ram-start: 0x20000000 + ram-size: 0x00020000 + flash-info: + - name: Internal Flash 16KB + start: 0x0000F000 + page-size: 0x00000040 + blocks: + - count: 64 + size: 0x00000100 + arg: 0 + blank-val: 0x0000000000000000 + fill-val: 0xCCCCCCCCCCCCCCCC + ptime: 100000 + etime: 1000000 + pname: cm0_core1 + - name: Family Flash + start: 0x80000000 + page-size: 0x00000100 + blocks: + - count: 64 + size: 0x00000100 + - count: 128 + size: 0x00000400 + debug-topology: + debugports: + - dpid: 0 + accessports: + - apid: 0 + index: 0 + datapatch: + - address: 0xE0040FCC + value: 0x00000011 + info: DEVTYPE + - address: 0xE0040FF0 + value: 0x0000000D + info: CIDR0 + - address: 0x1000FFFF + value: 0x000000CC + info: Family level datapatch + - dpid: 1 + accessports: + - apid: 1 + index: 0 + processors: + - pname: cm0_core0 + apid: 0 + reset-sequence: ResetSystem0 + - pname: cm0_core1 + apid: 1 + reset-sequence: ResetSystem1 diff --git a/tools/projmgr/test/src/ProjMgrUnitTests.cpp b/tools/projmgr/test/src/ProjMgrUnitTests.cpp index a1f709829..3217ff372 100644 --- a/tools/projmgr/test/src/ProjMgrUnitTests.cpp +++ b/tools/projmgr/test/src/ProjMgrUnitTests.cpp @@ -7317,9 +7317,8 @@ TEST_F(ProjMgrUnitTests, ImageOnlyMulticore) { argv[4] = (char*)"CM0"; EXPECT_EQ(0, RunProjMgr(5, argv, m_envp)); - const YAML::Node& cbuildRun = YAML::LoadFile(testinput_folder + "/ImageOnly/out/image-only-multicore+CM0.cbuild-run.yml"); - EXPECT_EQ("cm0_core0", cbuildRun["cbuild-run"]["output"][0]["pname"].as()); - EXPECT_EQ("cm0_core1", cbuildRun["cbuild-run"]["output"][1]["pname"].as()); + ProjMgrTestEnv::CompareFile(testinput_folder + "/ImageOnly/out/image-only-multicore+CM0.cbuild-run.yml", + testinput_folder + "/ImageOnly/ref/image-only-multicore+CM0.cbuild-run.yml"); } TEST_F(ProjMgrUnitTests, ListDebuggers) { @@ -7418,7 +7417,7 @@ TEST_F(ProjMgrUnitTests, DuplicateComponents) { EXPECT_EQ(1, RunProjMgr(8, argv, m_envp)); auto errStr = streamRedirect.GetErrorString(); EXPECT_NE(string::npos, errStr.find("error csolution: conflict: component 'RteTest:CORE' is listed multiple times")); - + argv[7] = (char*)"duplicateComponents_clayer"; EXPECT_EQ(1, RunProjMgr(8, argv, m_envp)); errStr = streamRedirect.GetErrorString();