diff --git a/tools/projmgr/include/ProjMgrParser.h b/tools/projmgr/include/ProjMgrParser.h index 9b0f617a8..15a67f0ce 100644 --- a/tools/projmgr/include/ProjMgrParser.h +++ b/tools/projmgr/include/ProjMgrParser.h @@ -128,6 +128,18 @@ struct MemoryItem { std::string algorithm; }; +/** + * @brief custom item containing + * scalar + * array + * map +*/ +struct CustomItem { + std::string scalar; + std::vector vec; + std::vector> map; +}; + /** * @brief debugger item containing * name of debug configuration @@ -135,6 +147,7 @@ struct MemoryItem { * debug clock speed * debug configuration file * start pname + * custom properties */ struct DebuggerItem { std::string name; @@ -142,6 +155,7 @@ struct DebuggerItem { std::string clock; std::string dbgconf; std::string startPname; + CustomItem custom; }; /** @@ -609,6 +623,7 @@ struct DebugAdapterDefaultsItem { std::string port; std::string protocol; std::string clock; + CustomItem custom; }; /** diff --git a/tools/projmgr/include/ProjMgrRunDebug.h b/tools/projmgr/include/ProjMgrRunDebug.h index 6d3fa0666..b3d4ee750 100644 --- a/tools/projmgr/include/ProjMgrRunDebug.h +++ b/tools/projmgr/include/ProjMgrRunDebug.h @@ -225,6 +225,9 @@ class ProjMgrRunDebug { const std::map& pnames); void CollectDebugTopology(const ContextItem& context, std::vector>> debugs, const std::map& pnames); + void CustomVecPushBack(std::vector& vec, const CustomItem& value); + CustomItem& CustomMapFind(std::vector>& customMap, const std::string& key); + void MergeCustomItems(const CustomItem& src, CustomItem& dst); }; #endif // PROJMGRRUNDEBUG_H diff --git a/tools/projmgr/include/ProjMgrWorker.h b/tools/projmgr/include/ProjMgrWorker.h index 482aa927b..c6e7a2db4 100644 --- a/tools/projmgr/include/ProjMgrWorker.h +++ b/tools/projmgr/include/ProjMgrWorker.h @@ -288,6 +288,7 @@ struct GdbServerItem { * debug configuration file * start pname * list of gdbserver items + * custom options */ struct DebuggerType { std::string name; @@ -297,6 +298,7 @@ struct DebuggerType { std::string dbgconf; std::string startPname; std::vector gdbserver; + CustomItem custom; }; /** diff --git a/tools/projmgr/include/ProjMgrYamlParser.h b/tools/projmgr/include/ProjMgrYamlParser.h index 243294cd1..5966a8f6c 100644 --- a/tools/projmgr/include/ProjMgrYamlParser.h +++ b/tools/projmgr/include/ProjMgrYamlParser.h @@ -352,6 +352,7 @@ class ProjMgrYamlParser { bool ParseLinker(const YAML::Node& parent, const std::string& file, std::vector& linker); void ParseRte(const YAML::Node& parent, std::string& rteBaseDir); void ParseDebugDefaults(const YAML::Node& parent, const std::string& file, DebugAdapterDefaultsItem& defaults); + void ParseCustom(const YAML::Node& parent, const std::vector& skip, CustomItem& custom); bool GetTypes(const std::string& type, std::string& buildType, std::string& targetType, std::string& pattern); bool ValidateCdefault(const std::string& input, const YAML::Node& root); bool ValidateCsolution(const std::string& input, const YAML::Node& root); @@ -365,6 +366,7 @@ class ProjMgrYamlParser { void ParsePortablePath(const YAML::Node& parent, const std::string& file, const std::string& key, std::string& value); void ParsePortablePaths(const YAML::Node& parent, const std::string& file, const std::string& key, std::vector& value); void EnsurePortability(const std::string& file, const YAML::Mark& mark, const std::string& key, std::string& value); + CustomItem GetCustomValue(const YAML::Node& node); }; diff --git a/tools/projmgr/schemas/common.schema.json b/tools/projmgr/schemas/common.schema.json index 5aec833f9..527e98760 100644 --- a/tools/projmgr/schemas/common.schema.json +++ b/tools/projmgr/schemas/common.schema.json @@ -2413,7 +2413,7 @@ "protocol": { "enum": [ "jtag", "swd" ], "description": "Selected debug protocol (jtag or swd)." }, "clock": { "type": "number", "description": "Selected debug clock speed (in Hz)." } }, - "additionalProperties": false + "additionalProperties": true } } } diff --git a/tools/projmgr/src/ProjMgrCbuildRun.cpp b/tools/projmgr/src/ProjMgrCbuildRun.cpp index 480774199..6e510464a 100644 --- a/tools/projmgr/src/ProjMgrCbuildRun.cpp +++ b/tools/projmgr/src/ProjMgrCbuildRun.cpp @@ -32,6 +32,8 @@ class ProjMgrCbuildRun : public ProjMgrCbuildBase { void SetAccessPortsNode(YAML::Node node, const std::vector& accessPorts); void SetDatapatchNode(YAML::Node node, const std::vector& datapatch); void SetGdbServerNode(YAML::Node node, const std::vector& gdbserver); + void SetCustomNodes(YAML::Node node, const CustomItem& debugger); + YAML::Node GetCustomNode(const CustomItem& value); }; ProjMgrCbuildRun::ProjMgrCbuildRun(YAML::Node node, @@ -110,6 +112,31 @@ void ProjMgrCbuildRun::SetDebuggerNode(YAML::Node node, const DebuggerType& debu } SetNodeValue(node[YAML_START_PNAME], debugger.startPname); SetGdbServerNode(node[YAML_GDBSERVER], debugger.gdbserver); + SetCustomNodes(node, debugger.custom); + } +} + +YAML::Node ProjMgrCbuildRun::GetCustomNode(const CustomItem& value) { + YAML::Node node; + if (!value.scalar.empty()) { + node = value.scalar; + } + else if (!value.vec.empty()) { + for (const auto& item : value.vec) { + node.push_back(GetCustomNode(item)); + } + } + else if (!value.map.empty()) { + for (const auto& [k, v] : value.map) { + node[k] = GetCustomNode(v); + } + } + return node; +} + +void ProjMgrCbuildRun::SetCustomNodes(YAML::Node node, const CustomItem& custom) { + for (const auto& [key, value] : custom.map) { + node[key] = GetCustomNode(value); } } diff --git a/tools/projmgr/src/ProjMgrRunDebug.cpp b/tools/projmgr/src/ProjMgrRunDebug.cpp index 0df2a9d0a..b15cad714 100644 --- a/tools/projmgr/src/ProjMgrRunDebug.cpp +++ b/tools/projmgr/src/ProjMgrRunDebug.cpp @@ -340,8 +340,13 @@ void ProjMgrRunDebug::CollectDebuggerSettings(const ContextItem& context, const if (!m_runDebug.debugger.clock.has_value() && !adapter.defaults.clock.empty()) { m_runDebug.debugger.clock = RteUtils::StringToULL(adapter.defaults.clock); } + // default custom options + m_runDebug.debugger.custom = adapter.defaults.custom; } } + + // merge custom options + MergeCustomItems(context.debugger.custom, m_runDebug.debugger.custom); } void ProjMgrRunDebug::CollectDebugTopology(const ContextItem& context, const vector>> debugs, @@ -632,3 +637,38 @@ bool ProjMgrRunDebug::GetDebugAdapter(const string& name, const DebugAdaptersIte } return false; } + +void ProjMgrRunDebug::CustomVecPushBack(vector& vec, const CustomItem& value) { + for (const auto& item : vec) { + if (item.scalar == value.scalar) { + return; + } + } + vec.push_back(value); +} + +CustomItem& ProjMgrRunDebug::CustomMapFind(vector>& customMap, const string& key) { + for (auto& [k, v] : customMap) { + if (key == k) { + return v; + } + } + customMap.push_back({ key, CustomItem() }); + return customMap.back().second; +} + +void ProjMgrRunDebug::MergeCustomItems(const CustomItem& src, CustomItem& dst) { + if (!src.scalar.empty()) { + dst.scalar = src.scalar; + } + else if (!src.vec.empty()) { + for (const auto& item : src.vec) { + CustomVecPushBack(dst.vec, item); + } + } + else if (!src.map.empty()) { + for (const auto& [k, v] : src.map) { + MergeCustomItems(v, CustomMapFind(dst.map, k)); + } + } +} diff --git a/tools/projmgr/src/ProjMgrWorker.cpp b/tools/projmgr/src/ProjMgrWorker.cpp index 9ccb92e94..ed768f0e6 100644 --- a/tools/projmgr/src/ProjMgrWorker.cpp +++ b/tools/projmgr/src/ProjMgrWorker.cpp @@ -2523,6 +2523,7 @@ bool ProjMgrWorker::ProcessDebuggers(ContextItem& context) { } } context.debugger.startPname = m_activeTargetSet.debugger.startPname; + context.debugger.custom = m_activeTargetSet.debugger.custom; } for (const auto& [filename, fi] : context.rteActiveProject->GetFileInstances()) { if (fi->HasAttribute("configfile")) { diff --git a/tools/projmgr/src/ProjMgrYamlParser.cpp b/tools/projmgr/src/ProjMgrYamlParser.cpp index df436fb13..3e924c87e 100644 --- a/tools/projmgr/src/ProjMgrYamlParser.cpp +++ b/tools/projmgr/src/ProjMgrYamlParser.cpp @@ -615,6 +615,7 @@ void ProjMgrYamlParser::ParseDebugger(const YAML::Node& parent, const string& fi ParseNumber(debuggerNode, file, YAML_CLOCK, debugger.clock); ParsePortablePath(debuggerNode, file, YAML_DBGCONF, debugger.dbgconf); ParseString(debuggerNode, YAML_START_PNAME, debugger.startPname); + ParseCustom(debuggerNode, { YAML_NAME, YAML_PROTOCOL, YAML_CLOCK, YAML_DBGCONF, YAML_START_PNAME }, debugger.custom); } } @@ -631,6 +632,7 @@ void ProjMgrYamlParser::ParseDebugDefaults(const YAML::Node& parent, const strin ParseNumber(defaultsNode, file, YAML_PORT, defaults.port); ParseString(defaultsNode, YAML_PROTOCOL, defaults.protocol); ParseNumber(defaultsNode, file, YAML_CLOCK, defaults.clock); + ParseCustom(defaultsNode, { YAML_PORT, YAML_PROTOCOL, YAML_CLOCK }, defaults.custom); } } @@ -1074,6 +1076,34 @@ void ProjMgrYamlParser::ParseImages(const YAML::Node& parent, const string& file } } +CustomItem ProjMgrYamlParser::GetCustomValue(const YAML::Node& node) { + CustomItem value; + if (node.IsScalar()) { + value.scalar = node.as(); + } + else if (node.IsSequence()) { + for (const auto& item : node) { + value.vec.push_back(GetCustomValue(item)); + } + } + else if (node.IsMap()) { + for (const auto& item : node) { + value.map.push_back({ item.first.as(), GetCustomValue(item.second) }); + } + } + return value; +} + +void ProjMgrYamlParser::ParseCustom(const YAML::Node& parent, const vector& skip, CustomItem& custom) { + for (const auto& node : parent) { + const auto& key = node.first.as(); + if (find(skip.begin(), skip.end(), key) != skip.end()) { + continue; + } + custom.map.push_back({ key, GetCustomValue(node.second) }); + } +} + // Validation Maps const set defaultKeys = { YAML_COMPILER, diff --git a/tools/projmgr/test/data/TestRunDebug/custom.cproject.yml b/tools/projmgr/test/data/TestRunDebug/custom.cproject.yml new file mode 100644 index 000000000..42a444d17 --- /dev/null +++ b/tools/projmgr/test/data/TestRunDebug/custom.cproject.yml @@ -0,0 +1,7 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/Open-CMSIS-Pack/devtools/main/tools/projmgr/schemas/cproject.schema.json + +project: + + components: + - component: Startup + - component: CORE diff --git a/tools/projmgr/test/data/TestRunDebug/custom.csolution.yml b/tools/projmgr/test/data/TestRunDebug/custom.csolution.yml new file mode 100644 index 000000000..087875d45 --- /dev/null +++ b/tools/projmgr/test/data/TestRunDebug/custom.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 + + target-types: + - type: TestHW + device: RteTest_ARMCM4_NOFP + target-set: + - set: + debugger: + name: Test Custom Adapter + custom-key: custom value + custom-key-overwrite: custom value overwrite + custom-array: + - value 1 + - value 2 + custom-map: + key: value + images: + - project-context: custom + + projects: + - project: custom.cproject.yml + + packs: + - pack: ARM::RteTest_DFP diff --git a/tools/projmgr/test/data/TestRunDebug/ref/custom+TestHW.cbuild-run.yml b/tools/projmgr/test/data/TestRunDebug/ref/custom+TestHW.cbuild-run.yml new file mode 100644 index 000000000..743ac7b28 --- /dev/null +++ b/tools/projmgr/test/data/TestRunDebug/ref/custom+TestHW.cbuild-run.yml @@ -0,0 +1,143 @@ +cbuild-run: + generated-by: csolution version 0.0.0+gb7a0c7b1 + solution: ../data/TestRunDebug/custom.csolution.yml + target-type: TestHW + target-set: + compiler: AC6 + device: ARM::RteTest_ARMCM4_NOFP + device-pack: ARM::RteTest_DFP@0.2.0 + output: + - file: out/custom/TestHW/custom.axf + info: generate by custom+TestHW + type: elf + load: image+symbols + system-resources: + memory: + - name: FLASH + access: rx + start: 0x00000000 + size: 0x00040000 + from-pack: ARM::RteTest_DFP@0.2.0 + - name: SRAM + access: rwx + start: 0x20000000 + size: 0x00020000 + from-pack: ARM::RteTest_DFP@0.2.0 + system-descriptions: + - file: ${CMSIS_PACK_ROOT}/ARM/RteTest_DFP/0.2.0/Device/ARM/SVD/ARMCM4.svd + type: svd + debugger: + name: Test Custom Adapter + protocol: jtag + clock: 40000000 + dbgconf: ../data/TestRunDebug/.cmsis/custom+TestHW.dbgconf + custom-adapter-key: custom adapter value + custom-key-overwrite: custom value overwrite + custom-map: + adapter-key: adapter value + key: value + custom-array: + - adapter item + - value 1 + - value 2 + custom-key: custom value + debug-vars: + vars: | + __var DbgMCU_CR = 0x00000007; // DBGMCU_CR: DBG_SLEEP, DBG_STOP, DBG_STANDBY + __var TraceClk_Pin = 0x00040003; // PE2 + __var TraceD0_Pin = 0x00040003; // 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/CortexM4Device.FLM + start: 0x00000000 + size: 0x00020000 + ram-start: 0x20000000 + ram-size: 0x00020000 + - algorithm: ${CMSIS_PACK_ROOT}/ARM/RteTest_DFP/0.2.0/Device/ARM/Flash/CortexM4SubFamily.FLM + start: 0x00000000 + size: 0x00040000 + ram-start: 0x20000000 + ram-size: 0x00020000 + - 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 + debug-topology: + debugports: + - dpid: 0 + jtag: + tapindex: 7 + accessports: + - apid: 1 + index: 10 + - apid: 2 + address: 0x00200000 + accessports: + - apid: 3 + address: 0x00300000 + - dpid: 1 + swd: + targetsel: 8 + swj: false + dormant: true + sdf: ${CMSIS_PACK_ROOT}/ARM/RteTest_DFP/0.2.0/Device/ARM/Debug/ARMCM4.sdf diff --git a/tools/projmgr/test/src/ProjMgrUnitTests.cpp b/tools/projmgr/test/src/ProjMgrUnitTests.cpp index f15c0d01f..39e9996ae 100644 --- a/tools/projmgr/test/src/ProjMgrUnitTests.cpp +++ b/tools/projmgr/test/src/ProjMgrUnitTests.cpp @@ -6735,6 +6735,39 @@ TEST_F(ProjMgrUnitTests, TestRunDebug) { testinput_folder + "/TestRunDebug/ref/run-debug+TestHW2.cbuild.yml"); } +TEST_F(ProjMgrUnitTests, TestRunDebugCustom) { + const string& debugAdaptersPath = etc_folder + "/debug-adapters.yml"; + const string& backup = RteFsUtils::BackupFile(debugAdaptersPath); + YAML::Node debugAdapters = YAML::LoadFile(debugAdaptersPath); + YAML::Node testAdapter; + testAdapter["name"] = "Test Custom Adapter"; + testAdapter["defaults"]["custom-adapter-key"] = "custom adapter value"; + testAdapter["defaults"]["custom-key-overwrite"] = "custom adapter key overwrite"; + testAdapter["defaults"]["custom-map"]["adapter-key"] = "adapter value"; + testAdapter["defaults"]["custom-array"][0] = "adapter item"; + debugAdapters["debug-adapters"].push_back(testAdapter); + ofstream debugAdaptersFile; + debugAdaptersFile.open(debugAdaptersPath, fstream::trunc); + debugAdaptersFile << debugAdapters << std::endl; + debugAdaptersFile.close(); + + char* argv[7]; + const string& csolution = testinput_folder + "/TestRunDebug/custom.csolution.yml"; + argv[1] = (char*)"convert"; + argv[2] = (char*)csolution.c_str(); + argv[3] = (char*)"-o"; + argv[4] = (char*)testoutput_folder.c_str(); + argv[5] = (char*)"--active"; + argv[6] = (char*)"TestHW"; + EXPECT_EQ(0, RunProjMgr(7, argv, m_envp)); + ProjMgrTestEnv::CompareFile(testoutput_folder + "/custom+TestHW.cbuild-run.yml", + testinput_folder + "/TestRunDebug/ref/custom+TestHW.cbuild-run.yml"); + + error_code ec; + fs::copy(fs::path(backup), fs::path(debugAdaptersPath), fs::copy_options::overwrite_existing, ec); + RteFsUtils::RemoveFile(backup); +} + TEST_F(ProjMgrUnitTests, TestNoDbgconf) { char* argv[7]; const string& csolution = testinput_folder + "/TestRunDebug/no-dbgconf.csolution.yml";