December 19, 2013

debugging pyOCD gdb (tutorial)

I finally found a root cause of pyOCD locking kinetis chip. It took me quite a while to get into gdb server implementation, still many functions are unveiled for me. I am going to give you few tips what I would do once something is not working, or even to be sure that the right binary is going to be flashed. That's was my case, when I knew that pyOCD is flashing wrong data to the wrong address.

To start with, please if you use gdb server, change logging to DEBUG. This information is in gdb_test.py file.

logging.basicConfig(level=logging.DEBUG)

What helped me a lot to just see how data are interpreted, disable erasing the chip and then flashing as well. The code below is from gdbserver.py file. Don't forget, if you change anything inside pyOCD, please rerun setup.py file with a command

python setup.py install

Comment out two lines that init() and eraseALL().
if ops == 'FlashErase':
    self.flash.init()
    self.flash.eraseAll()
    return self.createRSPPacket("OK")

Flashing is carried out by calling programPage. Once you comment that line out, your target won't be flashed, so you can add a breakpoint anywhere there, write data to a file and see how they are interpreted.
while len(self.flashData) > 0:
    size_to_write = min(self.flash.page_size, len(self.flashData))
    #if 0 is returned from programPage, security check failed
    if (self.flash.programPage(flashPtr, self.flashData[:size_to_write]) == 0):
        logging.error("Protection bits error, flashing has stopped")
        return None
    flashPtr += size_to_write

    self.flashData = self.flashData[size_to_write:]

    # print progress bar
    sys.stdout.write('\r')
    i = int((float(flashPtr)/float(bytes_to_be_written))*20.0)
    # the exact output you're looking for:
    sys.stdout.write("[%-20s] %d%%" % ('='*i, 5*i))
    sys.stdout.flush()

There are many debug files commented in the implementation, which are intended for debugging purposes. The one below is from FlashDone command case, once you enable it, the bad_bin contains entire data which are going to be written to the flash. You can compare it to your bin generated, reveal if it matches the data.

elif 'FlashDone' in ops :
    flashPtr = 0
    bytes_to_be_written = len(self.flashData)

    """
    bin = open(os.path.join(parentdir, 'res', 'bad_bin.txt'), "w+")
    
    i = 0
    while (i < bytes_to_be_written):
        bin.write(str(self.flashData[i:i+16]) + "\n")
        i += 16
    """

How did I solve that problem with locking the chip? The most important addition is the security bits check, which is crucial. If data at the address 0x400 are not set to 0xFF, flashing should be stopped. If it would not be stopped, the chip can get secured, depending on the 16 byte value at that address.
I added the following lines, this is why debug logging is good to have enabled, you can see how those bits are set and are going to be flashed. This would have unveiled that chip is going to get locked.

def checkSecurityBits(self, address, data):
    #error if security bits have unexpected values
    if (address == 0x400):
        for i in range(12):
            logging.debug("data[%d] at add 0x%X: 0x%X", i, i, data[i])
            if (data[i] != 0xff):
                return 0

        logging.debug("data[%d] at add 0x%X: 0x%X", i+3, i+3, data[i+3])
        logging.debug("data[%d] at add 0x%X: 0x%X", i+4, i+4, data[i+4])
        if ((data[i+3] != 0xff) or (data[i+4] != 0xff)):
            return 0

    return 1

I even locked sometimes the chip (only security bit or some other bits, not mass erase bit). If chip is locked, mbed msd drive does not need to appear! Don't get frustrated. Follow the lines below, and you might get mbed drive again.
To solve this problem, get into bootloader mode (to disable CMSIS-DAP which enables another interface to access the mcu chip), use external jlink or just openSDA jlink. Execute the commands I shared in the previos post:
unlocking kinetis chip with jlink console [http://embeddedworldweb.blogspot.com]
Don't be afraid if it returns error, sometimes it does but chip is functional again. Just reconnect your board, mbed drive should appear again if the chip is not bricked (like mass erase bit is set).

To get familiar with gdb serial protocol, this link helps
GDB Remote serial protocol [www.embecosm.com/appnotes/]

I will add more chips to pyOCD. Finding the root cause of locking the kinetis chip provided a really appreciated lesson. Happy to contribute to pyOCD.

Side note: the security check is on my branch at the moment - waiting for merge to the master branch. Don't use KL25Z with pyOCD from the origin master at the moment! You can lock your chip. Thanks for understanding.
To test the above features, here's link to my branch  dev issue#1 - security bits check [github.com/0xc0170]