pyocd / pyOCD

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

Adding CoreSight components missing from ROM tables #1586

Open rapgenic opened 1 year ago

rapgenic commented 1 year ago

I'm currently doing some experiments with a iMX RT1176 SoC. The debug architecture doesn't have much documentation, but by putting together all the pieces of information I think it is organised more or less the following way:

immagine

And this is the memory map (for the M7 core only)

immagine

From this errata we know that the CoreSight components in the CSSYS domain are not reported in any ROM, and in fact when running pyocd gdbserver I get the following:

0000402 I Target type is mimxrt1170_cm7 [board]
0000418 I DP IDR = 0x6ba02477 (v2 rev6) [dap]
0000465 I AHB-AP#0 IDR = 0x84770001 (AHB-AP var0 rev8) [discovery]
0000474 I AHB-AP#1 IDR = 0x24770011 (AHB-AP var1 rev2) [discovery]
0000485 I APB-AP#2 IDR = 0x54770002 (APB-AP var0 rev5) [discovery]
0000493 I AHB-AP#0 Class 0x1 ROM table #0 @ 0xe00fd000 (designer=00e:NXP part=88c) [rom_table]
0000525 I [0]<e00fe000:ROM class=1 designer=43b:Arm part=4c8> [rom_table]
0000527 I   AHB-AP#0 Class 0x1 ROM table #1 @ 0xe00fe000 (designer=43b:Arm part=4c8) [rom_table]
0000533 I   [0]<e00ff000:ROM class=1 designer=43b:Arm part=4c7> [rom_table]
0000533 I     AHB-AP#0 Class 0x1 ROM table #2 @ 0xe00ff000 (designer=43b:Arm part=4c7) [rom_table]
0000537 I     [0]<e000e000:SCS v7-M class=14 designer=43b:Arm part=00c> [rom_table]
0000542 I     [1]<e0001000:DWT v7-M class=14 designer=43b:Arm part=002> [rom_table]
0000544 I     [2]<e0002000:FPB v7-M class=14 designer=43b:Arm part=00e> [rom_table]
0000547 I     [3]<e0000000:ITM v7-M class=14 designer=43b:Arm part=001> [rom_table]
0000551 I   [1]<e0041000:ETM M7 class=9 designer=43b:Arm part=975 devtype=13 archid=4a13 devid=0:0:0> [rom_table]
0000556 I   [2]<e0042000:CTI CS-400 class=9 designer=43b:Arm part=906 devtype=14 archid=0000 devid=40800:0:0> [rom_table]
0000561 I [1]<e0043000:Trace Funnel CS-400 class=9 designer=43b:Arm part=908 devtype=12 archid=0000 devid=28:0:0> [rom_table]
0000607 I AHB-AP#1 Class 0x1 ROM table #0 @ 0xe00ff000 (designer=43b:Arm part=4c4) [rom_table]
0000610 I [0]<e000e000:SCS v7-M class=14 designer=43b:Arm part=00c> [rom_table]
0000613 I [1]<e0001000:DWT v7-M class=14 designer=43b:Arm part=002> [rom_table]
0000614 I [2]<e0002000:FPB v7-M class=14 designer=43b:Arm part=003> [rom_table]
0000616 I [3]<e0000000:ITM v7-M class=14 designer=43b:Arm part=001> [rom_table]
0000621 I [5]<e0041000:ETM M4 class=9 designer=43b:Arm part=925 devtype=13 archid=0000 devid=0:0:0> [rom_table]
0000626 I [7]<e0043000:Trace Funnel CS-400 class=9 designer=43b:Arm part=908 devtype=12 archid=0000 devid=28:0:0> [rom_table]
0000630 I [8]<e0042000:CTI CS-400 class=9 designer=43b:Arm part=906 devtype=14 archid=0000 devid=40800:0:0> [rom_table]
0000639 I CPU core #0 is Cortex-M7 r1p2 [cortex_m]
0000642 I FPU present: FPv5-D16-M [cortex_m]
0000690 I 4 hardware watchpoints [dwt]
0000692 I 8 hardware breakpoints, 1 literal comparators [fpb]
0000702 I 4 hardware watchpoints [dwt]
0000705 I 6 hardware breakpoints, 4 literal comparators [fpb]
0000727 I Semihost server started on port 4444 (core 0) [server]
0001092 I GDB server started on port 3333 (core 0) [gdbserver]

As you can see these components were not found.

Is there any way to tell pyocd to add them to the tree of the coresight components?

On a side note, nothing will exit the SWO/TPIU unless the funnels are configured, I'll try to make a pull request in the future to add support for those, once I'm able to fix this.

flit commented 1 year ago

Hi @rapgenic,

That's troublesome! And there are multiple issues in pyocd making it more so.

First, pyocd currently doesn't properly handle CoreSight components that are attached to an AP without a CPU core.

When pyocd performs CoreSight discovery, it builds an object graph, visible by running the show graph command from pyocd commander or gdb monitor command. This is an example from an STM32H750:

- "board": Board
  - "soc": Stm32h750ibtx
    - "Cortex-M7": CortexM
      - DWT
      - FPB
      - ITM

The STM32H750, like the RT1176, has an APB-AP with system-level CoreSight components (except with a valid ROM table 😉). But, as you can see, they aren't shown in the graph above.

Fortunately, there is already a GenericMemAPTarget target subclass that can be used to anchor CS components on the graph. I'll have to think a bit about how to detect that this needs to be done.

As for the missing ROM table… The "usual" way this is worked around in debuggers is to have a data patch mechanism, where the value read from specific addresses is overridden with a replacement value. This would effectively be used to create an artificial ROM table. It would also need to override the AP#2 ROM table base address register (this register is effectively in a separate address space). Unfortunately… pyocd currently doesn't have a data patch feature (it just hasn't been needed yet).

Fwiw, you can see a data patch example from the Open-CMSIS-Pack docs

Assuming you don't want to implement data patching (which would be nice, but I don't expect you to), you will need to create a pyocd.coresight.tpiu.TPIU object and attach it to the target object graph. (The graph methods are in pyocd.utility.graph.GraphNode.)

Finally, the gdbserver will need to pass the CoreSightTarget (session.target) object to SWVReader rather than the core—or perhaps in addition to the core. This will enable it to find a system-level TPIU. It also needs to know the core(s) in order to configure the ITM. Honestly, there's a lot that needs to be done to improve configurability of SWO/SWV.

Sorry there's not an easy solution!

rapgenic commented 1 year ago

Hi @flit thank you for the detailed answer, I didn't think this would be so complicated!

I had a look at the cmsis pack for the imxrt1176 and there is no trace of any data patch like the example you suggested, so for this case it wouldn't fix the problem, unless we tried to add the patch functionality here, maybe?

But even in that case, how would it work, given that those components are not associated to any of the two cores?

Assuming you don't want to implement data patching (which would be nice, but I don't expect you to), you will need to create a pyocd.coresight.tpiu.TPIU object and attach it to the target object graph. (The graph methods are in pyocd.utility.graph.GraphNode.)

The same question would apply here, where should I add the new node in the graph? Below one of the two cores?

Finally, the gdbserver will need to pass the CoreSightTarget (session.target) object to SWVReader rather than the core—or perhaps in addition to the core. This will enable it to find a system-level TPIU. It also needs to know the core(s) in order to configure the ITM. Honestly, there's a lot that needs to be done to improve configurability of SWO/SWV.

This at the moment is the least of my problems :smile:, in fact I'm using a shiny new orbtrace, which has its own host mux for handling swo/trace output.

On a side note, it would be nice to be able to configure the SWO(/TPIU in the future) in the correct way even if the probe doesn't support it or we don't want to use the pyocd internal swo receiver, like in case of the orbtrace.

Even though, as an alternative, my idea was to use one of the functions available in the user script, to fetch the corresponding nodes and initialize them using the available functions, something like:

def did_connect():
        itm = target.get_first_child_of_type(ITM)
        tpiu = target.get_first_child_of_type(TPIU)

        target.trace_start()

        itm.init()
        itm.enable()
        tpiu.init()

Also I guess my idea to add support for trace funnels is not worth it right now, as they live outside the core as well...

flit commented 1 year ago

Yeah, sorry it's so complicated…!

I had a look at the cmsis pack for the imxrt1176…

Right, the changes will have to be in the builtin RT1176 target(s). (It's possible to create a family class that is used for a Pack-based target type, but the changes would be similar.)

how would it work, given that those components are not associated to any of the two cores?

First, the object graph would look something like this:

- "board": Board
  - "soc": MIMXRT1176xxxxx_CM7
    - "Cortex-M7": CortexM
      - DWT
      - FPB
      - ITM
    - GenericMemAPTarget
      - TPIU

So, a generic non-core target is added under the SoC node, and the TPIU added under that.

Using data patch:

  1. Data patching would be implemented and applied at the DP and AP levels so it can filter all AP, DP, and memory transfers.
  2. CoreSight discovery has to be extended to create the GenericMemAPTarget when it discovers CoreSight nodes being created on an AP without an associated core.

Without data patch: an additional init sequence task would be used to create the GenericMemAPTarget and TPIU manually and attach them to the graph.

it would be nice to be able to configure the SWO(/TPIU in the future) in the correct way…

Agreed, that would be useful.

Extending the entire SWO/trace functionality in pyocd is something I'd really like to see. Trace funnels, config file based setup, even MTB/ETB would all be nice.

rapgenic commented 1 year ago

Hi @flit, at the moment I don't have enough knowledge of pyocd code to be able to implement the data patch functionality, however I've made a patch to enable the GenericMemAPTarget behaviour you described in this issue.

Ironically I cannot test it on my target :smile:, but if you want to have a look I pushed everything here: https://github.com/protech-engineering/pyOCD/tree/rapgenic/genericmemap

flit commented 1 year ago

No worries, I didn't expect you to jump in and implement data patch! 😄

The GenericMemAPTarget patch looks quite good!

It would affect more code, but it would be nice to replace the AP .core attribute with a generic .root_target attribute, for a Target object, instead of adding .generic_target. .core really is just the root of that part of the graph. The only place that actually reads that attributeisCoreSightComponent.factory()`, when it adds a component to the graph.

But it probably doesn't matter much. A lot of things related to the object graph needs to be reworked, anyway…

rapgenic commented 1 year ago

It would affect more code, but it would be nice to replace the AP .core attribute with a generic .root_target attribute

I force-pushed (sorry :sweat_smile:) something like what you said to the same branch as before if you want to have a look

flit commented 1 year ago

Thanks. And no worries about force pushing! I do that a lot myself to keep branches clean.

rapgenic commented 7 months ago

Hi @flit.

I was finally able to test the changeset to add the GenericMemAPTarget to the object graph (I am now working on a project with the STM32H745 mcu, which had the same problem as the IMXRT1176, i.e. the SWO peripheral on a different AP, however with a correct ROM table).

I was able to correctly identify both the SWO and the TPIU blocks and find them under the GenericMemAPTarget, and to obtain a working SWO output!

immagine

Here is the show graph output:

$ python3 -m pyocd commander
Connected to STM32H745ZITx [Running]: 001F00483432511630343838
pyocd> show graph
- "board": MbedBoard
  - "soc": Stm32h745zitx
    - "CM7": CortexM
      - DWT
      - FPB
      - ITM
    - GenericMemAPTarget
      - TPIU
      - TraceFunnel
      - TraceFunnel
      - TPIU
pyocd> 

I rebased my branch onto main, and if you are still interested I can open a PR.