pyocd / pyOCD

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

Load ELF to RAM #974

Closed ksigurdsson closed 3 years ago

ksigurdsson commented 4 years ago

This may not be a bug, it's more of a question but I couldn't find an appropriate forum to ask it on - sorry.

I'm trying to download a ELF file to RAM (not Flash) in my target (using the API). It seems like the 'load' command can accept an ELF file but complains that the address to download to is not Flash. The 'loadmem' command can download to RAM but only accepts a binary file.

So, can someone help by telling me if there's a way, using the API, to download an ELF into a 'RamRegion'?

Thanks

Hoohaha commented 4 years ago

you can use gdb to load elf to memory/flash. gdb commands:

target remote localhost:3333 load <path/to/elf> mon go q

flit commented 4 years ago

@Hoohaha is right that the best option right now is gdb. On the todo list is a new pyocd load subcommand that supports loading to RAM, as well as the internal support so that the Commander load command supports it as well.

ksigurdsson commented 4 years ago

Thanks for all the help. I'm using the API for some automated testing and it's working well.

Is it possible to call gdb commands from within a custom Python script (using the API) without the gdb server running?

I'm currently calling the 'load' command from within my script. Is this appropriate? Is this a command intended for the 'commander'?

I'm struggling to work out where the line is between API, GDB server and commander (which I'm guessing are both built against the API).

Thanks

flit commented 4 years ago

Glad to hear your using pyocd for testing!

It's not easily or cleanly possible to execute gdb commands from within a custom Python script. You can shell out to it and use --ex args if you have to. That's fine for some things, but can be relatively slow. Depending on what you're using gdb for, it may not be possible to integrate with a script using the pyocd Python API (for example, don't run gdb to set a breakpoint and expect the breakpoint to remain set after gdb exits).

You're right that the gdbserver and commander are built against the API—mostly. The gdbserver currently has some higher level functionality in it for things like RTOS thread awareness. This code is being (slowly, in the background) refactored into a reusable DebugController class.

The commands like load are mostly intended for interactive use. In most cases they are built on reusable code that is easier to use than the commands from within Python.

For instance, you can see the load command implementation here: https://github.com/pyocd/pyOCD/blob/692277901c178f9eea260a40f115a3408bfaa14e/pyocd/commands/commands.py#L623

It simple uses the FileProgrammer class here: https://github.com/pyocd/pyOCD/blob/692277901c178f9eea260a40f115a3408bfaa14e/pyocd/flash/file_programmer.py#L44

If you look at the FileProgrammer._program_elf() method, you can see how to extract segments from an ELF. For loading to RAM, you probably need to look at sections instead. Just write to RAM any section withsh_flags where SHF_WRITE is set (double check that matches the sections you need, though!). (The Wikipedia ELF page has a good overview, and there are other good introductions online.) The pyelftools package is used to parse ELFs.

I'll also see if I can find time to work on a generic ELF loader this weekend. No promises! 😄 (And no relocation to start with.)

flit commented 4 years ago

@ksigurdsson Can you tell me more about the ELFs that you need to load? Even better would be if you can attach an example.

Most Cortex-M applications are built to run from NVM. The C runtime startup code takes care of copying to and otherwise initialising RAM. So to load an ELF you just write the NVM contents and let the system boot—it's unnecessary to write anything to RAM, and indeed pointless. There are cases where you run Cortex-M apps entirely from RAM, but it's not too common. Thus my question. (You probably already know all this.)

ksigurdsson commented 4 years ago

Hi @flit,

Sure no problem. A bit of background probably helps here. I'm less familiar with how the software is structured as I'm an ASIC designer (started my career in the Coresight team working on ETM mainly).

I'm currently validating a new ASIC recently back from FAB. I've not yet "taught" pyOCD how to program the flash. I'm using pytest and pyOCD for automated tests where I download a test to RAM (initially - then flash later) set the PC to _start, set a breakpoint and resume. I've inherited a wacky linker script with an odd memory layout - all works fine using GDB but really would like to script/automate this using the pyOCD API.

Additionally, I eventually expect to download a flash loader to RAM and get the CPU to execute the loader (using pyOCD to control it) to get it to program the flash more quickly during production.

I'll look at attaching an ELF file tomorrow.

Thanks for all the help. I think pyOCD (specifically the API) is a perfect fit for what I'm doing.

flit commented 4 years ago

Cool! 😄 That kind of situation is how I originally got involved in pyOCD, though on the software engineer side.

What you describe makes perfect sense. Thanks for the details, the context helps a lot.

ksigurdsson commented 4 years ago

Hi @flit,

Here's a simple example ELF file that I'd like to load to RAM - hope it's useful.

Thanks

jlink-printf-test-release.zip

flit commented 4 years ago

Perfect, thanks!

flit commented 3 years ago

Fyi, there is a prototype implementation of load to RAM support available on my feature/load_command branch. Currently on this branch there is a separate 'load' subcommand, although actually it and 'flash' both support loading to RAM (same underlying code). Before upstreaming, 'load' will probably just be made an alias of 'flash'.

flit commented 3 years ago

Closing this issue. Support for loading to RAM was added in v0.32.0.