PyOCD部分源码解析
1. 探测下载器流程
检测下载器部分代码:
ConnectHelper.list_connected_probes()
allProbes = ConnectHelper.get_all_connected_probes(blocking=False)
allProbes = DebugProbeAggregator.get_all_connected_probes(unique_id=unique_id)
使用静态方法进行探测,probe目录下实现了几种常见的调试器,如CMSIS DAP、Jlink等,采用插件加载的方式加载这些probe的子类,这些子类统一继承自DebugProbe。
cmsis_dap_probe.py内的get_all_connected_probes方法描述了如何发现dap设备。
cmsis_access_cmsis_dap.py中的DAPAccessCMSISDAP是具体实现细节,DAPAccess.get_connected_devices(),在此方法中调用两个接口,一个是CMSIS-DAPv1 interfaces,另一个是CMSIS-DAPv2 interfaces
board.py中根据target_override参数来实例化target:
# Convert dashes to underscores in the target type, and convert to lower case.
target = normalise_target_type_name(target)
self._target_type = target
self.target = TARGET[self._target_type](session)#实例化
2.硬件相关
插入CMSIS DAP,使用pyocd list命令能够获取到设备信息:
(base) C:\Users\Administrator>pyocd list
# Probe/Board Unique ID Target
---------------------------------------------------------------------------
0 CMSIS-DAP by muselab-tech.com JCK CMSIS-DAP 0001A0000001 n/a
在设备管理器中可以看到设备类型和VID&PID:
3.loader子命令执行流程
(1)调用load_cmd.py中invoke方法
(2)ConnectHelper.session_with_chosen_probe方法获取session,该方法如下:
# Choose a probe.
probe = ConnectHelper.choose_probe(
blocking=blocking,
return_first=return_first,
unique_id=unique_id,
)
if probe is None:
return None
else:
return Session(probe, auto_open=auto_open, options=options, **kwargs)
choose_probe方法调用流程如下:
● 首先获取所有已连接的设备ConnectHelper.get_all_connected_probes(blocking=blocking, unique_id=unique_id);
● DebugProbeAggregator通过PROBE_CLASSES存储所有类型的下载器,如CMSIS-DAP等,通过load_plugin_classes_of_type(‘pyocd.probe’, PROBE_CLASSES, DebugProbe)方法扫描并注册基类为DebugProbe的类,load_plugin_classes_of_type返回所有继承自DebugProbe的类并添加到PROBE_CLASSES字典;
● pydapaccess.py中的get_connected_devices()方法获取已经连接的设备
● 调用dap_access_cmsis_dap.py中的get_connected_devices方法
● 通过调用_get_interfaces方法获取连接设备
● 分别通过v1_interfaces(USB_BACKEND)和v2_interfaces(USB_BACKEND_V2)来执行get_all_connected_interfaces
● interface目录下包含各种版本的DAP接口,可以通过环境变量来控制使用何种版本的接口,如果环境变量不指定,USB_BACKEND那么根据操作系统版本进行不同的操作,windows系统如果HidApiUSB.isAvailable,就优先使用HidApi,否则使用PyWinUSB.isAvailable就使用WinUSB,如果是MacOS则使用hidapiusb,如果是Linux则使用pyusb。
● USB_BACKEND_V2使用pyusb_v2
● 默认windows使用hidapi_backend.py
Session中board包含target的信息。
4.擦除流程
(1)ConnectHelper.session_with_chosen_probe获取session,session中的target为CoreSightTarget
(2)创建FlashEraser
(3)执行erase方法,该方法传递一个地址参数address,默认此处跟的是整片擦除,所以_mode为Mode.CHIP
(4)调用_chip_erase()方法
(5)执行以下代码:
if self._log_chip_erase:
LOG.info("Erasing chip...")
# Erase all flash regions. This may be overkill if either each region's algo erases
# all regions on the chip. But there's no current way to know whether this will happen,
# so prefer to be certain.
for region in self._session.target.memory_map.iter_matching_regions(type=MemoryType.FLASH):
if region.flash is not None:
if region.flash.is_erase_all_supported:
region.flash.init(region.flash.Operation.ERASE)
region.flash.erase_all()
region.flash.cleanup()
else:
self._sector_erase([(region.start, region.end)])
if self._log_chip_erase:
LOG.info("Chip erase complete")
首先在memory_map中查询类型为MemoryType.FLASH的区域,遍历所有region,如果支持整片擦除则调用整片擦除方法,否则使用sector擦除方法。
总结
本文仅揭示了PyOCD实现的冰山一角,建议读者通过以下路径深入探索:
- 研究pyocd.gdbserver模块的调试服务实现
- 分析CMSIS-Pack设备支持包的加载机制
- 跟踪SWD协议数据包在pyocd.coresight中的处理流程
- 实践自定义调试器驱动的开发
PyOCD通过清晰的模块划分和抽象层次设计,既保证了跨平台兼容性,又为二次开发提供了良好基础。理解其源码结构,有助于开发者针对特定需求进行深度定制和功能扩展。