After an nRF5340 executes a power-on, brown-out, watchdog timer, or pin reset it is not automatically accessible to debug probes. This is true even if the nRF5340's memory is completely blank --i.e. no settings have been written to the device to disable the debugger-- because Access Port Protection APPROTECT requires firmware on the nRF5340 to explicitly enable debugger access every time the device starts up. This action must be performed independently on both cores.
A simple procedure is used to allow debugger access to a core in case a reset is executed before firmware has been programmed to keep the device "unlocked":
- Perform an ERASEALL via the CTRL-AP
- Program the required firmware before a reset is executed
The nrfjprog --recover operation handles both of these steps automatically. If nrfjprog isn't an option --or isn't practical for some reason-- then the steps can be done manually.
Performing an ERASEALL
The nRF5340 DK has an onboard J-Link so Segger's JLink utility is a convenient way to demonstrate how to interact with the CTRL-AP over SWD. Two Debug Port (DP) registers need to be introduced first:
Setting the CSYSPWRUPREQ and CDBGPWRUPREQ bits in the CTRL/STAT DP powers up the device's debugger interface.
The APSEL field in the SELECT DP selects between the four possible Access Ports. The APBANKSEL is used to specify the most-significant four bits of the register address in the AP that you are accessing.
Only three JLink commands are required and they are all named intuitively:
- SWDWriteDP [ADDRESS] [VALUE]
- SWDReadAP [ADDRESS]
- SWDWriteAP [ADDRESS] [VALUE]
But there are two tricky things to remember:
- The ADDRESS parameter is automatically shifted left by two bits; Entering "SWDReadAP 1" is interpreted as "SWDReadAP 100b" or "SWDReadAP 4".
- The first invocation of SWDReadAP returns stale data so it is typically called at least twice.
The JLink utility is installed along with the Segger J-Link drivers (on Linux it is named JLinkExe). First plug an nRF5340 DK into the PC and launch the JLink utility. Then select the SWD interface:
J-Link>SWDSelect Select SWD by sending SWD switching sequence. Found SWD-DP with ID 0x6BA02477
Next the debug interface must be powered up:
J-Link>SWDWriteDP 1 0x50000000 Write DP register 1 = 0x50000000
The Identification Register (IDR) of one or more APs can be read as a sanity check. The first two APs should identify as Arm's AHB-AP (0x84770001) and the next two as Nordic's CTRL-AP (0x12880000). The SELECT DP's address is 8 (2<<2) and the IDR register's address is 0xFC (0xF<<4 | 3<<2). This is the application core's IDR:
J-Link>SWDWriteDP 2 0x020000F0 Write DP register 2 = 0x020000F0 J-Link>SWDReadAP 3 Read AP register 3 = 0x00000000 J-Link>SWDReadAP 3 Read AP register 3 = 0x12880000
Next, the application core's ERASEALL operation can be executed by writing 1 to register 4 (1<<2):
J-Link>SWDWriteDP 2 0x02000000 Write DP register 2 = 0x02000000 J-Link>SWDWriteAP 1 1 Write AP register 1 = 0x00000001
When ERASEALLSTATUS (2<<2) reads zero then the operation is complete:
J-Link>SWDReadAP 2 Read AP register 2 = 0x12880000 J-Link>SWDReadAP 2 Read AP register 2 = 0x00000001 J-Link>SWDReadAP 2 Read AP register 2 = 0x00000000
The same procedure can be used on the network core by substituting the index of its AP for the application core's AP:
J-Link>SWDWriteDP 2 0x03000000 Write DP register 2 = 0x03000000 J-Link>SWDWriteAP 1 1 Write AP register 1 = 0x00000001 J-Link>SWDReadAP 2 Read AP register 2 = 0xXXXXXXXX J-Link>SWDReadAP 2 Read AP register 2 = 0x00000001 J-Link>SWDReadAP 2 Read AP register 2 = 0x00000000
At this point the nRF5340 will remain accessible to the debugger until it is reset. Programming firmware to the cores can be done immediately without closing the JLink utility:
if 1 device NRF5340_XXAA_APP speed 4000 connect loadfile app_core_fw.hex device NRF5340_XXAA_NET loadfile net_core_fw.hex exit
The firmware
Applications that are built using NCS tag v1.4.99-dev1 or newer include a function that takes care of setting up the APPROTECT registers. Currently this is done in system_nrf53_approtect.h and the device remains unlocked by default unless either ENABLE_APPROTECT or ENABLE_APPROTECT_USER_HANDLING was defined as a preprocesor symbol during compilation. This means that a simple application that contains only an infinite loop in main() can be programmed to a core to keep it unlocked. Note that the unlock firmware must be compiled separately for each core, however. An example project is attached to this post.