Open rapgenic opened 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!
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...
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:
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.
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
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 attributeis
CoreSightComponent.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…
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
Thanks. And no worries about force pushing! I do that a lot myself to keep branches clean.
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!
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.
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:
And this is the memory map (for the M7 core only)
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: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.