Implement pyOCD SWD/JTAG programming support with dynamic target detection#36
Draft
andrewleech wants to merge 12 commits into
Draft
Implement pyOCD SWD/JTAG programming support with dynamic target detection#36andrewleech wants to merge 12 commits into
andrewleech wants to merge 12 commits into
Conversation
Author
|
Hi @Josverl I'm not sure what your appetite is for large changes or if this a direction you like, but I wanted to use it so claude build it :-) Also, I haven't code reviewed this at all yet, so feel free to not look at it at all either until I do so and un-draft the PR! |
Draft
Owner
|
If you build it, you test it😜 I have found the challenge that in the HIL tests needed. I'm not sure if I have the equipment to test it. Other than that, if others can benefit from it, I think it is useful |
Open
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #36 +/- ##
==========================================
- Coverage 75.55% 70.59% -4.96%
==========================================
Files 54 58 +4
Lines 3031 3809 +778
Branches 488 622 +134
==========================================
+ Hits 2290 2689 +399
- Misses 621 974 +353
- Partials 120 146 +26 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
…ction - Add SWD/JTAG programming as alternative to serial bootloader methods - Support for debug probe discovery and management - Automated target chip selection using dynamic detection - Optional pyOCD dependency via `pyocd` extra - Replace hardcoded target mappings with dynamic API-based detection - Parse MCU info from `sys.implementation._machine` strings - Fuzzy matching algorithm for target selection - Direct probe-based target detection with fallback to fuzzy matching - Extensible architecture for future OpenOCD/J-Link support - Add `--method pyocd` option for explicit SWD/JTAG programming - Add `--probe-id` option for specific debug probe selection - Maintain existing serial bootloader behavior as default - Clean integration with existing flash method selection - Abstract debug probe layer for extensibility - Target detector abstraction with registry system - Proper error handling and fallback mechanisms - Performance optimized with caching and lazy loading - `mpflash/flash/debug_probe.py` - Debug probe abstraction layer - `mpflash/flash/pyocd_probe.py` - pyOCD-specific probe implementation - `mpflash/flash/pyocd_flash.py` - pyOCD flash programming interface - `mpflash/flash/pyocd_targets.py` - Target detection wrapper functions - `mpflash/flash/dynamic_targets.py` - Dynamic target detection engine - `mpflash/cli_pyocd.py` - pyOCD-specific CLI commands (future) - `mpflash/common.py` - Add FlashMethod enum for different programming methods - `mpflash/flash/__init__.py` - Integrate pyOCD into flash method selection - `mpflash/cli_flash.py` - Add CLI options for pyOCD method and probe selection - `pyproject.toml` - Add optional pyOCD dependency - `mpflash/cli_download.py` - Fix unused pytest import - **No hardware requirements change** - existing serial methods remain default - **Automated target selection** - no manual target configuration needed - **Extensible design** - easy to add OpenOCD, J-Link, etc. in future - **Performance optimized** - direct API calls instead of subprocess shells - **Maintainable** - eliminates hardcoded target mappings ```bash mpflash flash mpflash flash --method pyocd mpflash flash --method pyocd --probe-id stlink uv sync --extra pyocd ``` None - all existing functionality preserved with same default behavior.
cli_flash_board previously returned 0/1/2 from the callback, but Click
ignores function return values for exit_code in standalone_mode, so the
CLI always exited 0 even on flash failure or user cancellation.
Switch to ctx.exit(N) so the documented exit codes (0 success, 1 flash
failure, 2 user cancellation) actually reach the shell and CliRunner.
Test adjustments:
- tests/integration/test_cli_integration.py:
* Remove xfail from test_flash_failure_handling and
test_interactive_parameter_prompting now that exit codes propagate.
* test_flash_failure_handling now asserts on mock calls instead of
loguru log output, which is order-dependent across the full suite.
- tests/cli/test_cli_flash.py:
* test_mpflash_connected_comports: when serial ports are detected the
test expects success, so make flash_tasks return a non-empty list
and stub show_mcus to keep the success path quiet.
test_complete_pyocd_workflow_success previously asserted on the loguru log message 'Flashed 1 boards' reaching result.output, which is fragile because loguru handler configuration can change across tests run earlier in the suite. The test passed in isolation but failed in full-suite runs. Replace the log-output assertion with assertions on the show_mcus mock: the mock must have been called once, with the boards returned by flash_tasks. This verifies the same code path without depending on loguru capture.
The flash_pyocd() implementation in mpflash/flash/pyocd_flash.py imports is_pyocd_supported and get_unsupported_reason from pyocd_core (not the _from_mcu variants), and probe selection happens inside PyOCDFlash, not via find_probe_for_target / get_pyocd_target_from_mcu. The previous tests patched names that do not exist on the pyocd_flash module, so the class was xfailed. Rewrite the three tests to: - patch mpflash.flash.pyocd_flash.is_pyocd_supported - patch mpflash.flash.pyocd_flash.get_unsupported_reason - assert PyOCDFlash is constructed with probe_id / auto_install_packs - simulate 'no probe' by having PyOCDFlash.flash_firmware raise the same MPFlashError the real code raises (probe lookup is internal to it now) Remove the @pytest.mark.xfail marker on TestFlashPyOCDFunction.
PyOCDFlash.__init__ calls detect_pyocd_target() and is_pyocd_available() (imported from pyocd_core) and stores the resulting target on self.target_type. PyOCDFlash.flash_firmware() looks up the probe via find_pyocd_probe() (defined in pyocd_flash itself), then calls probe.program_flash(fw, target_type, **options). The previous tests patched is_debug_programming_available, get_pyocd_target_dynamic and find_debug_probe on pyocd_flash, none of which exist there, so the whole class was xfailed. Rewrite all six tests to patch the correct names on the pyocd_flash module and to give Mock(spec=PyOCDProbe) a description attribute so the debug log line in flash_firmware does not blow up. Remove the @pytest.mark.xfail marker on TestPyOCDFlash.
Signed-off-by: Jos Verlinde <Jos_Verlinde@hotmail.com>
Signed-off-by: Jos Verlinde <Jos_Verlinde@hotmail.com>
see: pyocd/libusb-package#28 Signed-off-by: Jos Verlinde <Jos_Verlinde@hotmail.com>
- avoid hardcoded tempfile Signed-off-by: Jos Verlinde <Jos_Verlinde@hotmail.com>
Signed-off-by: Jos Verlinde <Jos_Verlinde@hotmail.com>
Signed-off-by: Jos Verlinde <Jos_Verlinde@hotmail.com>
….14+ Signed-off-by: Jos Verlinde <Jos_Verlinde@hotmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Major Features Added
pyOCD Integration
pyocdextraDynamic Target Detection
sys.implementation._machinestringsCLI Integration
--method pyocdoption for explicit SWD/JTAG programming--probeoption for specific debug probe selectionArchitecture Improvements
Technical Details
Files Added
mpflash/flash/debug_probe.py- Debug probe abstraction layermpflash/flash/pyocd_probe.py- pyOCD-specific probe implementationmpflash/flash/pyocd_flash.py- pyOCD flash programming interfacempflash/flash/pyocd_targets.py- Target detection wrapper functionsmpflash/flash/dynamic_targets.py- Dynamic target detection enginempflash/cli_pyocd.py- pyOCD-specific CLI commands (future)Files Modified
mpflash/common.py- Add FlashMethod enum for different programming methodsmpflash/flash/__init__.py- Integrate pyOCD into flash method selectionmpflash/cli_flash.py- Add CLI options for pyOCD method and probe selectionpyproject.toml- Add optional pyOCD dependencympflash/cli_download.py- Fix unused pytest importKey Benefits
Usage
Breaking Changes
None - all existing functionality preserved with same default behavior.