Mass-erasing TI CC13xx/CC26xx chips with OpenOCD

I recently had some custom PCBs made to be able to integrate a ZigBee coordinator into the Banana Pi R3 router. The currently best-supported SoC for running ZigBee coordinators is widely considered to be TI's CC2652P2 due to its generous RAM and Flash as well as the integrated 20dBm power amplifier. To minimize the BOM I went with a RF-BM-2652P2 module from RF-Star containing just that SoC and only two additional connectors, a female header to plug into the R3's GPIO and a 10-pin ARM-style JTAG connector for initial programming.

When I got the boards I soldered the module as well as the two connectors and hooked the JTAG up to my FT2232H breakout board as the official XDS110 probes from TI are currently unavailable due to a chip shortage. Theoretically this should work as OpenOCD has support for both the probe as well as the SoC. But with neither OpenOCD 0.11 nor 0.12-rc3 the JTAG chain even probed correctly. Here I should note that TI has made the (questionable) design decision to only support the relatively-uncommon cJTAG (a 2-wire variant of JTAG) by default, requiring a set of specific scan codes sent into the cJTAG interface to actually switch into normal 4-wire JTAG operation. This evidently didn't work as I didn't see any activity on the TDI wire. After lots of googling I eventually stumbled upon issue #375 in the OpenOCD issue tracker which recommended adding runtest 20 before the cJTAG to JTAG transition sequence. This just runs 20 idle cycles on the JTAG interface before starting the transition, essentially JTAG's equivalent of putting a sleep into code. Patching this into my OpenOCD configuration made the JTAG chain work immediately.

But I wasn't done yet. While the SoC now identified correctly on JTAG, the ARM debug adapter (DAP), needed to access the flash, failed to initialize. I reliably got Error: Invalid ACK (4) in DAP response which is a rather unhelpful error, even googling results in no useful results. At this point I tried to use this setup with a different CC2652P2 on another board I own, which worked perfectly. So at this point it's not an issue with my JTAG adapter or OpenOCD configuration, but the SoC itself seems to either be blocking access to the ARM debug adapter or the core together with the debug adapter is powered down. This was a bit of a suprise considering that this is a brand-new module and thus should theoretically be clean and have no firmware on it. My best guess is that this firmware is left over from factory testing. Now, most SoCs have a feature where one can essentially factory-reset the entire chip, disabling any debug protection but also deleting all firmware on the chip.

These TI chips do in fact have a mass erase feature, though one needs to look in the 1700-page reference manual to find it. Under section 5.8 Debug Features Supported Through WUC TAP there is the CHIP_ERASE_REQ operation which does erase everything from the chip. Before I can use this functionality however I first need to be able to access the Wake Up Controller Test Access Point, which is a dedicated JTAG TAP. TI has their own JTAG router on these chips, which they call ICEPick. In Table 5-5 of the reference manual there is an overview of the various TAPs available to be enabled in the JTAG router. The TAP in question is called "AON WUC" here, available on the 5th test bank. OpenOCD recently (in unreleased verison 0.12) got support for not just enabling the debug banks but also the test banks through ICEPick. Enabling TAPs is done with icepick_c_tapenable followed by the TAP for the ICEPick itself, followed by the bank index. Debug banks start from 0, test banks from 16. So test bank 5 is at index 21. I guessed the instruction register length (irlen) based on the documented commands. The complete WUC TAP definition looks like this:

jtag newtap $_CHIPNAME wuc -irlen 4 -ircapture 0x1 -irmask 0xf -disable
jtag configure $_CHIPNAME.wuc -event tap-enable "icepick_c_tapenable $_CHIPNAME.jrc 21"

Now I have a JTAG TAP for the Wake Up Controller and can finally issue the documented commands. The IR value (0x01) needs to be shifted into the instruction register using irscan, the data register needs to be filled with the documented command bit and shifted in using drscan. After each command I run another 20 idle cycles just to make sure that the previous command is executed. This might not be necessary, but it takes negligible time. So issuing the mass erase looks like this:

# CHIP_ERASE_REQ
irscan $_CHIPNAME.wuc 0x01 -endstate IRPAUSE
drscan $_CHIPNAME.wuc 8 0x2 -endstate DRPAUSE
runtest 20

# MCU_VD_RESET_REQ
irscan $_CHIPNAME.wuc 0x01 -endstate IRPAUSE
drscan $_CHIPNAME.wuc 8 0x20 -endstate DRPAUSE
runtest 20

After running this, I tried connecting with my regular debug configuration again and the DAP came up immediately. So the chips were indeed not clean and that was the reason the DAP didn't connect.


If you want a complete solution for the CC2652, here is a OpenOCD script combining everything:

source [find target/icepick.cfg]
source [find target/ti-cjtag.cfg]

if { [info exists CHIPNAME] } {
        set _CHIPNAME $CHIPNAME
} else {
        set _CHIPNAME cc26x2
}

#
# WUC TAP
#
jtag newtap $_CHIPNAME wuc -irlen 4 -ircapture 0x1 -irmask 0xf -disable
jtag configure $_CHIPNAME.wuc -event tap-enable "icepick_c_tapenable $_CHIPNAME.jrc 21"

#
# ICEpick-C (JTAG route controller)
#
if { [info exists JRC_TAPID] } {
        set _JRC_TAPID $JRC_TAPID
} else {
        set _JRC_TAPID 0x3bb4102f
}
jtag newtap $_CHIPNAME jrc -irlen 6 -ircapture 0x1 -irmask 0x3f -expected-id $_JRC_TAPID -ignore-version
jtag configure $_CHIPNAME.jrc -event setup "jtag tapenable $_CHIPNAME.wuc"
# A start sequence is needed to change from 2-pin cJTAG to 4-pin JTAG
jtag configure $_CHIPNAME.jrc -event post-reset "ti_cjtag_to_4pin_jtag $_CHIPNAME.jrc"

init

# CHIP_ERASE_REQ
irscan $_CHIPNAME.wuc 0x01 -endstate IRPAUSE
set _res [drscan $_CHIPNAME.wuc 8 0x2 -endstate DRPAUSE]
echo [format %x $_res]
runtest 20

# MCU_VD_RESET_REQ
irscan $_CHIPNAME.wuc 0x01 -endstate IRPAUSE
set _res [drscan $_CHIPNAME.wuc 8 0x20 -endstate DRPAUSE]
echo [format %x $_res]
runtest 20

reset_config srst_once
adapter srst delay 100

exit
cc26x2-mass-erase.cfg

This can then be used from either the command line or another script loading this one. This is mine:

source [find interface/ftdi/minimodule.cfg]
transport select jtag
adapter speed 100
source ./cc26x2-mass-erase.cfg