Anyone using pyocd?

G’day All,

I previously done a little development on STM Nucleo boards - and when I got started I had pyocd working first so never looked into openocd too much. I’ve managed to get a similar pyocd workflow setup and running with the Daisy Seed.

Any reasons for or against using pyocd?

If people are interested I could share how I got it all installed and setup - might make a nice addition to the wiki and build environment? Let me know.

For info - I’m using an stlink-v3mini to flash and debug and I do my development on linux with basic makefiles and vim, so I can’t say too much about IDE support.

Cheers

I’m all for having more details and more setup info, at least on the forums. You’ll make someone’s day when they’re struggling with workflow/setup problems and frantically googling.

For context, I’m doing my development on Linux with an stlink-v3mini.

Here’s what I did to get it (mostly) working.

Install pyOCD - follow the destructions on GitHub - pyocd/pyOCD: Open source Python library for programming and debugging Arm Cortex-M microcontrollers

I cloned the repo and installed it locally to get recent developments - don’t know how necessary that is.

python3 setup.py install --user

Next step is to get some packs to support it:

pyocd pack -u ;# to update the pack list
pyocd pack -f ;# to see what’s available
pyocd pack -i ;# to install

In this case -

pyocd pack -i STM32H750IBKx

Make a pyocd_user.py - I’m not sure this is the right way to do things - beware…
This file has to go into the dir that your run pyocd/gdb from
The main purpose is to tell pyocd how to write to the onchip flash:

# For Daisy Seed
def will_connect(board):

    # For STM32H750xB devices: a 128-Kbyte user Flash memory block containing one
    # user sector of 128 Kbytes (4 K Flash words).
    # /data/daisy/cmsis/CMSIS/Flash/STM32H7xx_CM7.FLM
    flash = pyocd.core.memory_map.FlashRegion(
                                        name="flash",
                                        start=0x08000000,
                                        length=0x20000,
                                        blocksize=0x100,
                                        flm="/data/daisy/cmsis/CMSIS/Flash/STM32H7xx_CM7.FLM"
                                        )
    target.memory_map.add_region(flash)

    # Create the DTCMRAM region
    dtcmram = pyocd.core.memory_map.RamRegion(
                                        name="dtcram",
                                        start=0x20000000,
                                        length=0x20000
                                        )

    # Add the SDRAM region to the memory map.
    target.memory_map.add_region(dtcmram)


    # Create the new 64M SDRAM region
    sdram = pyocd.core.memory_map.RamRegion(
                                        name="sdram",
                                        start=0xc0000000,
                                        length=0x4000000
                                        )

    # Add the SDRAM region to the memory map.
    target.memory_map.add_region(sdram)

    # Create QSPIFLASH - took blocksize from libdaisy/src/per/qspi.c
    # TODO: No ALGO for this - can't keep code in it yet 
    qspiflash = pyocd.core.memory_map.FlashRegion(
                                        name="qspiflash",
                                        start=0x90000000,
                                        length=0x800000,
                                        blocksize=0x1000
                                        )

    # Add the SDRAM region to the memory map.
    target.memory_map.add_region(qspiflash)

#
# Don't know if this is 100% needed 
#
# The DBGMCU registers are not reset by a system reset, only by a power on reset. They are
# accessible to the debugger via the APB-D bus at base address 0xE00E1000. They are also
# accessible by the processor core at base address 0x5C001000.
# Note: the DBGMCU is not a standard CoreSight component. Therefore, it does not appear
# in the system ROM table.
#
# DBGMCU configuration register (DBGMCU_CR)
# Address offset: 0x004
# Reset value: 0x0000 0000
# 
# See also CMSIS/Debug/STM32H742_743_753_750.dbgconf

DBGMCU_CR = 0xE00E1004
DBGMCU_APB3FZ1 = 0xE00E1034
DBGMCU_APB1LFZ1 = 0xE00E103C
DBGMCU_APB2FZ1 = 0xE00E104C
DBGMCU_APB4FZ1 =  0xE00E1054

def did_reset():
    # Set STANDBY, STOP, and SLEEP bits all to 1.
    LOG.info("Setting DBGMCU_CR to 0x7 after reset")
    target.write32(DBGMCU_CR, 0x7)
    target.write32(DBGMCU_APB3FZ1, 0x0)
    target.write32(DBGMCU_APB1LFZ1, 0x0)
    target.write32(DBGMCU_APB2FZ1, 0x0)
    target.write32(DBGMCU_APB4FZ1, 0x0)

The FLM file mentioned in there is extracted from the previously installed cmsis pack.
I’ll try and get in touch with the pyocd folks to see if it’s the “right thing to do”.

To flash your code:

pyocd flash --target stm32h750ibkx <elf|bin>

In one window:

pyocd gdbserver --target stm32h750ibkx

In another:

arm-none-eabi-gdb

I put this in a .gdbinit file to connect:

target extended localhost:3333

monitor reset halt

info variables

I haven’t figured out how to load updated binaries from gdb yet - working on that.
But I have been able to do some basic debugging this way - setting break points, looking at variables, stepping through code etc.

Good luck.

So I’ve been in touch with one of the pyocd developers (very helpful!) and I think he’s even going to get a Daisy Seed which will only improve pyocd support - hooray.

In the meantime, with their help I’ve managed to put together something that allows stand alone flashing or loading from gdb. I’ve updated the pyocd_user.py as follows:

 # For Daisy Seed
def will_connect():

    # For STM32H750xB devices: a 128-Kbyte user Flash memory block containing one
    # user sector of 128 Kbytes (4 K Flash words).
    # 256 bit write = page_size of 32 Bytes
    # see /data/daisy/cmsis/Keil.STM32H7xx_DFP.pdsc
    internal_flash = target.memory_map.get_first_matching_region(name="FLASH_Bank1")
    LOG.info("Updating %s settings", internal_flash.name)
    flash = pyocd.core.memory_map.FlashRegion(
                                        name=internal_flash.name,
                                        start=0x08000000,
                                        length=0x20000,
                                        blocksize=0x20000,
                                        page_size=0x20,
                                        access='rx',
                                        is_boot_memory=True,
                                        flm="/data/daisy/cmsis/CMSIS/Flash/STM32H7x_128k.FLM"
                                        )
    target.memory_map.remove_region(internal_flash)
    target.memory_map.add_region(flash)
    LOG.info("%s", repr(flash))

    # Create the new 64M SDRAM region
    sdram = pyocd.core.memory_map.RamRegion(
                                        name="sdram",
                                        start=0xc0000000,
                                        length=0x4000000,
                                        is_default=False,
                                        is_powered_on_boot=False
                                        )

    # Add the SDRAM region to the memory map.
    target.memory_map.add_region(sdram)

    # Create QSPIFLASH - took blocksize from libdaisy/src/per/qspi.c
    # TODO: No ALGO for this - can't keep code in it yet 
    qspiflash = pyocd.core.memory_map.FlashRegion(
                                        name="qspiflash",
                                        start=0x90000000,
                                        length=0x800000,
                                        blocksize=0x1000
                                        )

    # Add the SDRAM region to the memory map.
    target.memory_map.add_region(qspiflash)

#
# Don't know if this is 100% needed 
#
# The DBGMCU registers are not reset by a system reset, only by a power on reset. They are
# accessible to the debugger via the APB-D bus at base address 0xE00E1000. They are also
# accessible by the processor core at base address 0x5C001000.
# Note: the DBGMCU is not a standard CoreSight component. Therefore, it does not appear
# in the system ROM table.
#
# DBGMCU configuration register (DBGMCU_CR)
# Address offset: 0x004
# Reset value: 0x0000 0000
# 
# See also CMSIS/Debug/STM32H742_743_753_750.dbgconf
# and /data/daisy/cmsis/Keil.STM32H7xx_DFP.pdsc

DBGMCU_CR = 0xE00E1004
DBGMCU_APB3FZ1 = 0xE00E1034
DBGMCU_APB1LFZ1 = 0xE00E103C
DBGMCU_APB2FZ1 = 0xE00E104C
DBGMCU_APB4FZ1 =  0xE00E1054

def did_connect():
    # Set STANDBY, STOP, and SLEEP bits all to 1.
    # set preset values (enable all debug clocks by default)
    # DBGMCU_CR[18] CKDBGD3EN, [17] CKDBGD2EN, [16] CKDBGD1EN
    # DBGMCU_CR[22] D3DBGCKEN, [21] D1DBGCKEN
    # 
    # From DM00314099.pdf:
    # D3DBGCKEN: bit 22
    # D1DBGCKEN: bit 21
    # TRACECLKEN: bit 20
    val = target.read32(DBGMCU_CR)
    val |= (0x00700000 | 0x00000007)
    LOG.info("Setting DBGMCU_CR to 0x%x after connect", val)
    target.write32(DBGMCU_CR, val)
    target.write32(DBGMCU_APB3FZ1, 0x0)
    target.write32(DBGMCU_APB1LFZ1, 0x0)
    target.write32(DBGMCU_APB2FZ1, 0x0)
    target.write32(DBGMCU_APB4FZ1, 0x0)

I’ve also added targets to the main Makefile to allow programming, debugging or stand alone pyocd command mode:

/data/daisy/pod_test$ make -n pyocd
pyocd cmd -f 20m --script …/DaisyExamples/libdaisy/core/pyocd_user.py --target stm32h750ibkx

/data/daisy/pod_test$ make -n program-pyocd
pyocd flash -f 20m --script …/DaisyExamples/libdaisy/core/pyocd_user.py --target stm32h750ibkx build/pod_test.elf

/data/daisy/pod_test$ make -n pyocd-gdbserver
pyocd gdbserver -f 20m --script …/DaisyExamples/libdaisy/core/pyocd_user.py --target stm32h750ibkx

Cheers

2 Likes