pyocd / pyOCD

Open source Python library for programming and debugging Arm Cortex-M microcontrollers
https://pyocd.io
Apache License 2.0
1.11k stars 477 forks source link

`DebugPortStart` sequence not executed when multiple definitions are present in the pack #1587

Open rapgenic opened 1 year ago

rapgenic commented 1 year ago

Hi, I have another issue regarding the IMXRT1176 :smile:

I'm trying to experiment with both the builtin target and the cmsis pack one. The builtin targets allow to debug only one core at a time, so I'm trying to use the cmsis pack.

On gdbserver start i get the following error when trying to access (I think) the cortex m4 debug port (verbose log):

0022830 D [Errno 13] Access denied (insufficient permissions) while trying to interrogate a USB device (VID=2b7e PID=0171). This can probably be remedied with a udev rule. See <https://github.com/pyocd/pyOCD/tree/master/udev> for help. [pyusb_backend]
0022832 D [Errno 13] Access denied (insufficient permissions) while trying to interrogate a USB device (VID=2b7e PID=0171). This can probably be remedied with a udev rule. See <https://github.com/pyocd/pyOCD/tree/master/udev> for help. [pyusb_v2_backend]
0022833 D Project directory: /workspaces/test [session]
0022833 D Loading config from: /workspaces/test/pyocd.yml [session]
0022836 D Project directory: /workspaces/test [session]
0022836 D Loading config from: /workspaces/test/pyocd.yml [session]
0022836 D Loading user script: /workspaces/test/pyocd_user.py [session]
0022844 D CMSIS-DAP v2 probe 2E60708163008E06: firmware version v1.3.1-0-gd096575-dirty, protocol version 2.1.0 [dap_access_cmsis_dap]
0023057 D using family class IMXRT for MIMXRT1176DVMAA (matched against MIMXRT1176DVMAA) [pack_target]
0023369 I Target type is mimxrt1176dvmaa [board]
0023376 D Running task load_svd [sequencer]
0023376 D Running task pre_connect [sequencer]
0024150 D Running task dp_init [sequencer]
0024155 D Running task lock_probe [sequencer]
0024160 D Running task get_probe_capabilities [sequencer]
0024166 D Running task connect [sequencer]
0024235 D Default wire protocol selected; using SWD [dap]
0024251 D Sending deprecated SWJ sequence to select SWD [swj]
0024314 I DP IDR = 0x6ba02477 (v2 rev6) [dap]
0024320 D Running task clear_sticky_err [sequencer]
0024320 D Running task power_up_debug [sequencer]
0109215 D Running task check_version [sequencer]
0109215 D Running task unlock_probe [sequencer]
0109226 D Running task unlock_device [sequencer]
0109231 D Running task create_discoverer [sequencer]
0109232 D Running task discovery [sequencer]
0109232 D Running task find_aps [sequencer]
0109334 D Running task create_aps [sequencer]
0109339 D Running task create_ap.0 [sequencer]
0109396 D AHB-AP#0 default HPROT=3 HNONSEC=0 [ap]
0109412 D AHB-AP#0 implemented HPROT=f HNONSEC=1 [ap]
0109424 I AHB-AP#0 IDR = 0x84770001 (AHB-AP var0 rev8) [discovery]
0109429 D Running task create_ap.1 [sequencer]
0109472 D AHB-AP#1 default HPROT=3 HNONSEC=0 [ap]
0109488 D AHB-AP#1 implemented HPROT=3 HNONSEC=0 [ap]
0109510 I AHB-AP#1 IDR = 0x24770011 (AHB-AP var1 rev2) [discovery]
0109510 D Running task create_ap.2 [sequencer]
0109580 D APB-AP#2 default HPROT=0 HNONSEC=0 [ap]
0109585 D APB-AP#2 implemented HPROT=0 HNONSEC=0 [ap]
0109607 I APB-AP#2 IDR = 0x54770002 (APB-AP var0 rev5) [discovery]
0109607 D Running task find_components [sequencer]
0109607 D Running task init_ap.0 [sequencer]
0109656 I AHB-AP#0 Class 0x1 ROM table #0 @ 0xe00fd000 (designer=00e:NXP part=88c) [rom_table]
0109684 I [0]<e00fe000:ROM class=1 designer=43b:Arm part=4c8> [rom_table]
0109684 I   AHB-AP#0 Class 0x1 ROM table #1 @ 0xe00fe000 (designer=43b:Arm part=4c8) [rom_table]
0109712 I   [0]<e00ff000:ROM class=1 designer=43b:Arm part=4c7> [rom_table]
0109717 I     AHB-AP#0 Class 0x1 ROM table #2 @ 0xe00ff000 (designer=43b:Arm part=4c7) [rom_table]
0109755 I     [0]<e000e000:SCS v7-M class=14 designer=43b:Arm part=00c> [rom_table]
0109777 I     [1]<e0001000:DWT v7-M class=14 designer=43b:Arm part=002> [rom_table]
0109788 I     [2]<e0002000:FPB v7-M class=14 designer=43b:Arm part=00e> [rom_table]
0109804 I     [3]<e0000000:ITM v7-M class=14 designer=43b:Arm part=001> [rom_table]
0109809 D     [4]<fff41002 not present> [rom_table]
0109815 D     [5]<fff42002 not present> [rom_table]
0109857 I   [1]<e0041000:ETM M7 class=9 designer=43b:Arm part=975 devtype=13 archid=4a13 devid=0:0:0> [rom_table]
0109900 I   [2]<e0042000:CTI CS-400 class=9 designer=43b:Arm part=906 devtype=14 archid=0000 devid=40800:0:0> [rom_table]
0109905 D   [3]<1ff02002 not present> [rom_table]
0109943 I [1]<e0043000:Trace Funnel CS-400 class=9 designer=43b:Arm part=908 devtype=12 archid=0000 devid=28:0:0> [rom_table]
0109948 D [2]<fff4a002 not present> [rom_table]
0109953 D [3]<1ff03002 not present> [rom_table]
0109958 D Running task init_ap.1 [sequencer]
0110011 E Transfer error while reading AHB-AP#1 ROM table:  [ap]
Traceback (most recent call last):
  File "/usr/local/lib/python3.10/dist-packages/pyocd/probe/cmsis_dap_probe.py", line 651, in read_ap_repeat_callback
    values = result()
  File "/usr/local/lib/python3.10/dist-packages/pyocd/probe/pydapaccess/dap_access_cmsis_dap.py", line 1112, in reg_read_repeat_cb
    res = transfer.get_result()
  File "/usr/local/lib/python3.10/dist-packages/pyocd/probe/pydapaccess/dap_access_cmsis_dap.py", line 157, in get_result
    self.daplink.flush()
  File "/usr/local/lib/python3.10/dist-packages/pyocd/utility/concurrency.py", line 29, in _locking
    return func(self, *args, **kwargs)
  File "/usr/local/lib/python3.10/dist-packages/pyocd/probe/pydapaccess/dap_access_cmsis_dap.py", line 881, in flush
    self._read_packet()
  File "/usr/local/lib/python3.10/dist-packages/pyocd/utility/concurrency.py", line 29, in _locking
    return func(self, *args, **kwargs)
  File "/usr/local/lib/python3.10/dist-packages/pyocd/probe/pydapaccess/dap_access_cmsis_dap.py", line 1157, in _read_packet
    decoded_data = cmd.decode_data(raw_data)
  File "/usr/local/lib/python3.10/dist-packages/pyocd/probe/pydapaccess/dap_access_cmsis_dap.py", line 480, in decode_data
    data = self._decode_transfer_data(data)
  File "/usr/local/lib/python3.10/dist-packages/pyocd/probe/pydapaccess/dap_access_cmsis_dap.py", line 386, in _decode_transfer_data
    self._check_response(data[2])
  File "/usr/local/lib/python3.10/dist-packages/pyocd/probe/pydapaccess/dap_access_cmsis_dap.py", line 366, in _check_response
    raise DAPAccessIntf.TransferTimeoutError()
pyocd.probe.pydapaccess.dap_access_api.DAPAccessIntf.TransferTimeoutError

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/local/lib/python3.10/dist-packages/pyocd/coresight/ap.py", line 834, in find_components
    cmpid.read_id_registers()
  File "/usr/local/lib/python3.10/dist-packages/pyocd/coresight/rom_table.py", line 124, in read_id_registers
    regs = self.ap.read_memory_block32(self.top_address + self.IDR_READ_START, self.IDR_READ_COUNT)
  File "/usr/local/lib/python3.10/dist-packages/pyocd/utility/concurrency.py", line 29, in _locking
    return func(self, *args, **kwargs)
  File "/usr/local/lib/python3.10/dist-packages/pyocd/coresight/ap.py", line 1210, in _read_memory_block32
    resp += self._read_block32_page(addr, n//4)
  File "/usr/local/lib/python3.10/dist-packages/pyocd/coresight/ap.py", line 1168, in _read_block32_page
    resp = self.dp.read_ap_multiple(self.address.address + self._reg_offset + MEM_AP_DRW, size)
  File "/usr/local/lib/python3.10/dist-packages/pyocd/coresight/dap.py", line 1004, in read_ap_multiple
    return read_ap_multiple_cb()
  File "/usr/local/lib/python3.10/dist-packages/pyocd/coresight/dap.py", line 994, in read_ap_multiple_cb
    return result_cb()
  File "/usr/local/lib/python3.10/dist-packages/pyocd/probe/cmsis_dap_probe.py", line 658, in read_ap_repeat_callback
    raise self._convert_exception(exc) from exc
pyocd.core.exceptions.TransferTimeoutError
0110021 D Running task create_cores [sequencer]
0110033 I IMXRT Boot Mode: Boot From Fuses [target_imxrt]
0110066 I CPU core #0 is Cortex-M7 r1p2 [cortex_m]
0110098 I FPU present: FPv5-D16-M [cortex_m]
0110116 D selected core #0 [soc_target]
0110122 D Running task configure_core_reset [sequencer]
0110122 D updated DFP core #0 reset types: {<ResetType.SW: 2>, <ResetType.SW_EMULATED: 5>, <ResetType.HW: 1>, <ResetType.SW_CORE: 4>, <ResetType.SW_SYSTEM: 3>} [pack_target]
0110127 I Setting core #0 (cm7) default reset sequence to ResetSystem [pack_target]
0110133 D Running task create_components [sequencer]
0110138 D Creating DWT component [discovery]
0110175 I 4 hardware watchpoints [dwt]
0110181 D Creating FPB component [discovery]
0110197 I 8 hardware breakpoints, 1 literal comparators [fpb]
0110203 D fpb has been disabled [fpb]
0110209 D Creating ITM component [discovery]
0110236 D Running task check_for_cores [sequencer]
0110242 D Running task halt_on_connect [sequencer]
0110247 D halting core 0 [cortex_m]
0110273 D Running task post_connect [sequencer]
0110279 D Running task post_connect_hook [sequencer]
0110284 D Running task create_flash [sequencer]
0110291 D flash algo: [stack=0x1ffe5350; 0x5350 b] [b2=0x1ffe5450,+0x5450] [b1=0x1ffe5350,+0x5350] [code=0x1ffe555c,+0x555c,0x2aa4 b] (ram=0x1ffe0000, 0x8000 b) [flash_algo]
0110299 D flash algo: [stack=0x1ffe5360; 0x5360 b] [b2=0x1ffe5460,+0x5460] [b1=0x1ffe5360,+0x5360] [code=0x1ffe5560,+0x5560,0x2aa0 b] (ram=0x1ffe0000, 0x8000 b) [flash_algo]
0110301 D flash algo: [stack=0x20005300; 0x5300 b] [b2=0x20005400,+0x5400] [b1=0x20005300,+0x5300] [code=0x2000550c,+0x550c,0x2af4 b] (ram=0x20000000, 0x8000 b) [flash_algo]
0110309 D flash algo: [stack=0x200052e0; 0x52e0 b] [b2=0x200053e0,+0x53e0] [b1=0x200052e0,+0x52e0] [code=0x200054e4,+0x54e4,0x2b1c b] (ram=0x20000000, 0x8000 b) [flash_algo]
0110309 D Running task notify [sequencer]
0110368 D Setting vector catch to 0x00000001 [cortex_m]
0110469 I Semihost server started on port 4444 (core 0) [server]
0110986 I GDB server started on port 3333 (core 0) [gdbserver]

Looking at the log it seems that the debug sequence DebugPortStart is not executed, which I think is responsible to configure correctly the port and make it accessible, besides being defined in the cmsis pack:

        <sequences>
          <sequence name="dpstart">...</sequence>
          <sequence Pname="cm4" name="DebugPortStart">...</sequence>
          <sequence Pname="cm7" name="DebugPortStart">...</sequence>
        </sequences>

Looking at the code I think that it's not executed because there are two Pnames available. However shouldn't they be both executed in this case?

flit commented 1 year ago

Sorry about that, that's a known issue. And, it's an issue with the Open-CMSIS-Pack (OCP) spec, and arguably with the RT1176 pack. The OCP spec is designed with the traditional single-core debugger in mind, so cases like this where you want to debug multiple cores but the pack defines different connect sequences for each core are simply not handled by the spec. Pack can be written to work around this, but the RT1176 pack isn't.

I do have a patch that at least corrects pyocd's behaviour to match the spec, though I haven't had time to fully test it yet. https://github.com/flit/pyOCD/tree/bugfix/debug_port_debug_seq_pname_support

This patch changes pyocd so it will only execute the DebugPortStart sequence for the core specified by the primary_core option. It's definitely not ideal, but that's the best that can be done with the OCP spec right now.

Making the builtin RT1176 target properly support multicore would be nice, but I don't have an RT1176 board to test with so I can't do the work myself.

You could also say there's another issue related to multicore in pyocd: there's not a defined way to start a secondary (non-boot) core running so it can be debugged. I think this is why the builtin RT1176 target is separated into M7/M4 targets. Though it could be worked around.

So, aside from the other trace component related issue, for multicore RT1176 development I'd recommend using the pack, plus a user script that releases the other core and possibly other config (like maybe SDRAM init).

rapgenic commented 1 year ago

Thank you for the explanation!

It's clear that this SoC is particularly complicated! I guess that for more advanced things one would need to use the NXP IDE with their own tools...

Anyway, I've made some advancements. I've discovered that, with the pack, pyocd can access the second core AP when it's already started, which means that at least I should be able to attach to a running target. Not ideal, but I've seen that most of the time booting up the system is not too difficult, there's plenty of template code from the manufacturer for that.

Anyways, the second core is still not configured, even using the pack! I've tracked this down to the target/family/target_imxrt.py file, where the create_cores sequence is overridden and made to create only the first core instance (with a special CortexM7_IMXRT class)! Is there a reason to be like that? Commenting that out actually creates two gdbserver instances, but I guess that also the CortexM7_IMXRT class serves its purpose...

Thanks for the patch, I can have a try and see if it's working, even though I don't think it'll make much of a difference, I think that the DebugPortStart sequences inside the pack are doing more or less the same initialization pyocd is already doing hardcoded.

flit commented 1 year ago

You should be able to use a user script with the pack target to ensure that the second core is running. Well, that's the theory…

You're right that the DebugPortStart patch won't fix the issue with the IMXRT family class not creating the second core.

The builtin RT1176 don't inherit from the IMXRT family class. So it's probably a bug that this family class is being used for the pack target.

I'll create a patch for you to test that fixes the family class matching. Or you can temporarily remove this line: https://github.com/pyocd/pyOCD/blob/0189b2f4888b2775edd4b2be3caede4e05a9ffa6/pyocd/target/family/__init__.py#L42

rapgenic commented 1 year ago

You're right that the DebugPortStart patch won't fix the issue with the IMXRT family class not creating the second core.

Actually I just tried the patch, and it doesn't yet work (meaning it won't execute the sequence at all). I think this is due to this check:

https://github.com/flit/pyOCD/blob/5a7f6bc359218b54b8620c879412d6ea047ed043/pyocd/coresight/dap.py#L441 (I can't get the GitHub preview to work...)

The self.has_debug_sequence('DebugPortSetup') will return false if multiple definitions of the sequence exist, but the pname function parameter is not specified...

Or you can temporarily remove this line:

This works. Just tested it and can detect and connect to both the cores!

flit commented 1 year ago

Thanks for testing and debugging! Will work on a fix.

Fyi, #1594 removes the family class match expression. It looks like there is nothing extra in the builtin targets that isn't present in the debug sequences, so there's no need to use the family class for any IMXRT pack-base target.

flit commented 1 year ago

Ok, the issue with the bugfix/debug_port_debug_seq_pname_support branch should be fixed. It was tested on an IMXRT685. Quite a different device, but it also has a DebugPortStart sequence with a pname.