Nucleo STLINK for LPC8xx – behind the scenes

This post is summary of the challenges it I was faced with. I write it down hoping that I may save others some time. Maybe a reader can enlightne me on any misconceptions form my side.

Getting a Nucleo F103RB STM32 to work as a STLINK adapter for an LPC810 is really simple. It has been described by Rick in cortex-m0+ SWD debugging on the cheap. I had it working in an hour. Only four issues:

1. I tried to get it working with OpenOCD 0.9.0, which has some modified syntax for flashing the LPC.
2. I did not get the nRESET connection to work properly. Luckily, SWD can perform a reset by writing a register in the LPC.
3. Some images flashed correctly. Others failed with an error code 9 in OpenOCD.
4. The most annoying: Ricks approach works, but all interrupts did not get through. So no blinking LEDS in the basic examples.

So after the first success I started ironing out the few remaining issues. Oh boy!

No interrupts, No counters, No Blink

This took me the most time. Reading datasheets, reading the source code of OpenOCD. It appeared that interrupts can be disabled when halted for a step, and re-enabled making the step. Maybe something wrong in the logic there? Nothing found. Wait for interrupt __WFI() should be avoided when debugging. Removed it from my code. No luck. Maybe it is the way the JeeLabs Embello type of apps are build. Lets try the NXP LPCXpresso way. No luck. No interrupts. So after two days I gave up on this. It was a deal breaker.

Then with a little bit of luck I found a clue. When I tried to get the hardware reset line to work, I found that the interrupts where disabled after flashing a firmware image. And when a hardware reset was performed after flashing, with the next launch of the arm-none-eabi-gdb debugger, interrupts just worked while stepping through the code.

By now I was already really deep into OpenOCD. So solution easy. Make a target event hook to gdb-flash-write-end. Perform a reset init here, and it should work. Not. It appeared just a tiny bit too early. Gdb instructs openOCD to flash with the load command, but after flashing, load does more work. A.o. it set the program counter to the firmware entry point, and queries a thread status. Trying to simulate this in the mentioned hook did not work.

So the remaining option is to send it a bit later, from the Eclipse CDT Debug Configuration. And then finally success! All interrupts work. The solution is very simple after all. By no means the solution reflects the frustration that the issues caused. It is this tiny line “mon reset init“:

EclipseDebugConfig4

I really have no idea why the flash-write-end hook did not work. Maybe the flash process had overwritten some table to interrupt vectors? But why could a normal initialization of the firmware (I was able to step through the code) not restore this? I see that the first 512 bytes of flash memory get overwritten when executing the first instruction. When launching, the program halts at a startup vector. Flash 0x000 -0x1FF looks strange. Press the play triangle in Eclipse, pause and the flash memory contains the proper information. Although this is strange, I do not think it is related to the missing interrupt problem.

Hard wired reset

The Nucleo STLINK interface has a hardwired reset connection to its own STM32 target. It also exposes it on the SWD connector. Rick wired this line up to his LPC812, but did not use it looking at the OpenOCD configuration. I thought it should be possible to use hardwired reset, when pin 8 of the LPC810 remains configured as nRESET.

I connected the reset line, started downloading a firmware to discover that the reset line was pulled low, but stayed low forever. So which component caused this? The LPC810 or the native Nucloe target the STM32? I added pull-up resistors, played around will all possible settings of the reset configuration in OpenOCD. I cutted the SB12 on the Nucleo to disconnect the native target, to find that the line was still pulled low. Then when being pulled low, I disconnected the reset line between STLink and LPC810 to see that the LPC810 recovered, but the Nucleo STLink stayed low.

Even worse. The STLINK only recovered by unplugging the USB cable. This made me realize that the line stayed low, because the Nucleo STLINK firmware had crashed. So me hypothesis is: When the STLINK pulls the nSRST low, then the LPC810 gets reset. During the reset someway or another the STLINK starts a handshake with the target, but does not get the proper response. Then the firmware of the STLINK crashes before it can switch the reset line high again.

Flashing error code 9

Annoyingly, when I was trying different compiler settings to get the interrupts to work while debugging, I found that suddenly my image did not flash anymore. Could this be a spoiled openocd configuration. Reverting back to a known good situation did not help. So lets see in the openOCD code what the error code 9 means? Ah! Sector not Prepared. Reading the code of LPC2000.c in more detail revealed that the flash process indeed tried to write to a sector that was not prepared for writing. Further study of the code showed that the problem is actually a coding error in lpc2000.c in OpenOCD (both in 0.8.0 and 0.9.0).

The problem:

The LPC8xx flash memory is divided in sectors of 1kB. The LPC810 has 4 sectors giving it 4kB flash in total. To write flash, OpenOCD first copies a chunk into RAM of the LPC8xx, after which the rAM is copied to flash. But the flash is write protected, so first the LPC8xx is told that certain sectors of flash will be written to. As the chunk must be in RAM, and RAM is only 1kB in the small LPC810, and some other stuff is still in RAM, only 256 bytes are written per chunk.

It appeared that all chunks but the last are written as 256 bytes. If the remaining bytes to be written is smaller than 256, than suddenly lpc2000.c decides to write 512 bytes! Normally this is no problem. Most images gets flashed correctly.

Now that I am typing this. Might using 512 bytes of RAM instead of the intended 256 corrupt something in RAM that is used for the interrupts that didn’t come through?

But when the total image size is less then 256 bytes short of a sector boundary, then writing the final chunk as 512 bytes crosses a sector boundary. The code that decides to prepare a certain number of sectors is not aware if the issue. So suddenly an unprepared sector gets written. As example, my flash image is 2000 bytes. lpc2000.c prepares sectors 0 and 1 (2048 bytes in total). The last chunk is not written as 256 bytes (up to the exact sector 1 end at byte 2047), but writes just into sector 2 (up to byte 2303). Sector was not prepared for this. Neither was I.

The offending code om line 1126 of lpc2000.c is this:

 

while (bytes_remaining > 0) {
    uint32_t thisrun_bytes;
    if (bytes_remaining >= lpc2000_info->cmd51_max_buffer)
        thisrun_bytes = lpc2000_info->cmd51_max_buffer;
    else if (bytes_remaining >= 1024)
        thisrun_bytes = 1024;
    else if ((bytes_remaining >= 512) || (!lpc2000_info->cmd51_can_256b))
        thisrun_bytes = 512;
    else if ((bytes_remaining >= 256) || (!lpc2000_info->cmd51_can_64b))
        thisrun_bytes = 256;
    else
       thisrun_bytes = 64;

    /* Prepare sectors */
    param_table[0] = first_sector;
    param_table[1] = last_sector;

 

The problem is when the code arrives in the second else and sets thisrun_bytes = 512, while cmd51_max_buffer = 256.

I have reported the issue and three possible solutions to the openocd-devel mailing list. A solution has been prepared by an expert, and is now waiting for review. I hope it can be merged soon to the openOCD main archive.

Differences between openocd 0.8.0 and 0.9.0

There are some differences in addressing the STLINK adapters between OpenOCD 0.8.0 and 0.9.0. The positive side is that OpenOCD-0.9.0 will have good LPC8xx support. The difference are mainly in the way taps and targets are created:

0.8.0 cfg

hla newtap $_CHIPNAME cpu -expected-id $_CPUTAPID
target create $_TARGETNAME hla_target -chain-position $_TARGETNAME

0.9.0 cfg

swj_newdap $_CHIPNAME cpu -irlen 4 -expected-id $_CPUTAPID
target create $_TARGETNAME cortex_m -chain-position $_TARGETNAME

Note: Paul Fertser of the openOCD mail list informed me that the 0.8.0 way actually is the pre-0.8.0 way. It is how the original approach of Rick, which works in 0.8.0, and no longer in 0.9.0.

Furthermore I found two issues with the 0.9.0 LPC8xx support:

  1. The default tap-id (CPUTAPID = 0x0bb11477) is wrong for the LPC810, so it needs to be set in higher level .cfg files to CPUTAPID = 0x0bc11477 . I will check this for other version of the LPC8xx.
  2. the communication speed to the LPC810 is set extremely low. Taking ages to flash or step through code. I override this to a recommended value (adapter_khz = 2000).

Proper configuration files can be downloaded form here for 0.8.0 and from here for 0.9.0.

 

 

3 thoughts to “Nucleo STLINK for LPC8xx – behind the scenes”

  1. Hi.
    One important thing to note is that the LPC processors check the ISP pin at power up. This is pin P0_12 for the LPC811. If this pin is low after reset the processor runs bootrom code.

    One way to stop this is to write to the CRP flash register to force NO_ISP mode on powerup.

    Using GCC I added the following to the system_LPC8xx.c file:
    #define NO_ISP 0x4E697370
    #define CRP1_MAGIC 0x12345678
    #define CRP2_MAGIC 0x87654321
    #define CRP3_MAGIC 0x43218765
    #define CURRENT_CRP_SETTING NO_ISP
    __attribute__ ((section(“.crp”),used)) const uint32_t CRP_WORD = CURRENT_CRP_SETTING;

    Then added the following to the linker .ld file:
    .text : ALIGN(4)
    {
    FILL(0xff)
    KEEP(*(.isr_vector))

    *(.after_vectors*)
    /*start CPR section definition*/
    . = 0x000002FC;
    KEEP(*(.crp))

    /* end CPR section definition*/

    *(.text*)
    *(.rodata .rodata.*)
    . = ALIGN(4);

    } > MFlash16

    Debug this then you have the repower the processor to enter this new mode.

    MPC

  2. Hello,

    I know that isn’t the best place to put this doubt but I don’t find a correct place.
    I tried to create a project with uart comunication using functions with the swm_8xx.h library, but when I’m going to compile gives an error “undefined reference to `Chip_SWM_MovablePinAssign'”. The code is the following: Chip_SWM_MovablePinAssign(SWM_U0_TXD_O, 7); I think that is corret. I found some people in web with the same error and one solution is adding -llibrary (https://www.lpcware.com/content/forum/couldnt-compile-libraries-projects-0). I would like to know in your application “PicosARM” where I can add this solution to add the multiples libraries?

    Best Regards,
    Tiago

Leave a Reply

Your email address will not be published. Required fields are marked *