Open microbit-carlos opened 2 years ago
Doesn't look like the --erase chip
flag is being applied, but something different is happening because in this case it takes a few more senconds to throw the error:
$ pyocd load --erase chip CALL_ME_MICROBIT.hex --verbose
0001569:INFO:board:Target type is nrf52833
0001611:INFO:dap:DP IDR = 0x2ba01477 (v1 rev2)
0001685:INFO:ap:AHB-AP#0 IDR = 0x24770011 (AHB-AP var1 rev2)
0001707:INFO:ap:AP#1 IDR = 0x02880000 (AP var0 rev0)
0001709:INFO:target_nRF52:NRF52833 not in secure state
0001802:INFO:rom_table:AHB-AP#0 Class 0x1 ROM table #0 @ 0xe00ff000 (designer=244 part=00d)
0001836:INFO:rom_table:[0]<e000e000:SCS v7-M class=14 designer=43b part=00c>
0001854:INFO:rom_table:[1]<e0001000:DWT v7-M class=14 designer=43b part=002>
0001857:INFO:rom_table:[2]<e0002000:FPB v7-M class=14 designer=43b part=003>
0001860:INFO:rom_table:[3]<e0000000:ITM v7-M class=14 designer=43b part=001>
0001862:INFO:rom_table:[4]<e0040000:TPIU M4 class=9 designer=43b part=9a1 devtype=11 archid=0000 devid=ca1:0:0>
0001865:INFO:rom_table:[5]<e0041000:ETM M4 class=9 designer=43b part=925 devtype=13 archid=0000 devid=0:0:0>
0001866:INFO:cortex_m:CPU core #0 is Cortex-M4 r0p1
0001870:INFO:cortex_m:FPU present: FPv4-SP-D16-M
0001871:INFO:dwt:4 hardware watchpoints
0001873:INFO:fpb:6 hardware breakpoints, 4 literal comparators
0001880:INFO:load_cmd:Loading /Users/microbit-carlos/Downloads/CALL_ME_MICROBIT.hex
[ ] 1%0011993:CRITICAL:__main__:flash program page failure (address 0x00000000; result code 0x67)
Traceback (most recent call last):
File "/Users/microbit-carlos/workspace/microbit-foundation/DAPLink-mbef-fork/venv/lib/python3.6/site-packages/pyocd/__main__.py", line 150, in run
status = cmd.invoke()
File "/Users/microbit-carlos/workspace/microbit-foundation/DAPLink-mbef-fork/venv/lib/python3.6/site-packages/pyocd/subcommands/load_cmd.py", line 120, in invoke
file_format=self._args.format)
File "/Users/microbit-carlos/workspace/microbit-foundation/DAPLink-mbef-fork/venv/lib/python3.6/site-packages/pyocd/flash/file_programmer.py", line 171, in program
self._loader.commit()
File "/Users/microbit-carlos/workspace/microbit-foundation/DAPLink-mbef-fork/venv/lib/python3.6/site-packages/pyocd/flash/loader.py", line 293, in commit
keep_unwritten=self._keep_unwritten)
File "/Users/microbit-carlos/workspace/microbit-foundation/DAPLink-mbef-fork/venv/lib/python3.6/site-packages/pyocd/flash/builder.py", line 503, in program
flash_operation = self._chip_erase_program_double_buffer(progress_cb)
File "/Users/microbit-carlos/workspace/microbit-foundation/DAPLink-mbef-fork/venv/lib/python3.6/site-packages/pyocd/flash/builder.py", line 779, in _chip_erase_program_double_buffer
raise FlashProgramFailure('flash program page failure', address=current_addr, result_code=result)
pyocd.core.exceptions.FlashProgramFailure: flash program page failure (address 0x00000000; result code 0x67)
As expected, pyocd cmd
has the same results:
$ pyocd cmd --verbose
0001346:INFO:board:Target type is nrf52833
0001372:INFO:dap:DP IDR = 0x2ba01477 (v1 rev2)
0001418:INFO:ap:AHB-AP#0 IDR = 0x24770011 (AHB-AP var1 rev2)
0001436:INFO:ap:AP#1 IDR = 0x02880000 (AP var0 rev0)
0001439:INFO:target_nRF52:NRF52833 not in secure state
0001454:INFO:rom_table:AHB-AP#0 Class 0x1 ROM table #0 @ 0xe00ff000 (designer=244 part=00d)
0001477:INFO:rom_table:[0]<e000e000:SCS v7-M class=14 designer=43b part=00c>
0001516:INFO:rom_table:[1]<e0001000:DWT v7-M class=14 designer=43b part=002>
0001535:INFO:rom_table:[2]<e0002000:FPB v7-M class=14 designer=43b part=003>
0001559:INFO:rom_table:[3]<e0000000:ITM v7-M class=14 designer=43b part=001>
0001594:INFO:rom_table:[4]<e0040000:TPIU M4 class=9 designer=43b part=9a1 devtype=11 archid=0000 devid=ca1:0:0>
0001606:INFO:rom_table:[5]<e0041000:ETM M4 class=9 designer=43b part=925 devtype=13 archid=0000 devid=0:0:0>
0001622:INFO:cortex_m:CPU core #0 is Cortex-M4 r0p1
0001626:INFO:cortex_m:FPU present: FPv4-SP-D16-M
0001629:INFO:dwt:4 hardware watchpoints
0001631:INFO:fpb:6 hardware breakpoints, 4 literal comparators
Connected to NRF52833 [Sleeping]: 9904360249624e45005160190000003b000000009796990b
pyocd> erase
0028549:INFO:eraser:Erasing chip...
0037704:INFO:eraser:Done
pyocd> erase 0 4096
0103317:INFO:eraser:Erasing sector 0x00000000 (4096 bytes)
Error: flash erase sector failure (address 0x00000000; result code 0x67)
Traceback (most recent call last):
File "/Users/microbit-carlos/workspace/microbit-foundation/DAPLink-mbef-fork/venv/lib/python3.6/site-packages/pyocd/commands/repl.py", line 96, in run_one_command
self.context.process_command_line(line)
File "/Users/microbit-carlos/workspace/microbit-foundation/DAPLink-mbef-fork/venv/lib/python3.6/site-packages/pyocd/commands/execution_context.py", line 299, in process_command_line
invoc.handler(invoc)
File "/Users/microbit-carlos/workspace/microbit-foundation/DAPLink-mbef-fork/venv/lib/python3.6/site-packages/pyocd/commands/execution_context.py", line 369, in execute_command
cmd_object.execute()
File "/Users/microbit-carlos/workspace/microbit-foundation/DAPLink-mbef-fork/venv/lib/python3.6/site-packages/pyocd/commands/commands.py", line 896, in execute
eraser.erase([self.addr])
File "/Users/microbit-carlos/workspace/microbit-foundation/DAPLink-mbef-fork/venv/lib/python3.6/site-packages/pyocd/flash/eraser.py", line 76, in erase
self._sector_erase(addresses)
File "/Users/microbit-carlos/workspace/microbit-foundation/DAPLink-mbef-fork/venv/lib/python3.6/site-packages/pyocd/flash/eraser.py", line 145, in _sector_erase
flash.erase_sector(sector_addr)
File "/Users/microbit-carlos/workspace/microbit-foundation/DAPLink-mbef-fork/venv/lib/python3.6/site-packages/pyocd/flash/flash.py", line 374, in erase_sector
raise FlashEraseFailure('flash erase sector failure', address=address, result_code=result)
pyocd.core.exceptions.FlashEraseFailure: flash erase sector failure (address 0x00000000; result code 0x67)
pyocd>
This is because the micro:bit uses Nordic's SoftDevices. The normal flash algo cannot erase the SoftDevice sector because the SoftDevice locks itself when installed.
You need to use either chip or mass erase to remove the SoftDevice.
Btw, with pyocd erase --sector
you have to tell pyocd what sectors you want to erase.
The error code 0x67 is from the flash algo. So it would most likely be the Nordic flash driver's error code.
Fyi, Nordic includes an "sde" version of the nRF52 and nRF51 flash algos in their nRF CMSIS Device Family Pack, where "sde" seems to mean "SoftDevice erase". I haven't tried it out to see how it behaves differently, or if it can be used in all cases in place of the normal algo.
I can confirm that a pyocd erase --mass
worked for me.
Here's some documentation that I'm writing to cover this in a new "Target family notes" section to be added to the docs.
The nRF51 and nRF52 series have support for so-called “SoftDevice” firmware, which implements Nordic's Bluetooth LE or other wireless protocol API. When firmware containing a SoftDevice is loaded, the SoftDevice region of flash is locked. In order to reprogram the flash sectors containing the SoftDevice image, a mass erase must first be performed. This can potentially cause issues with flash programming if one is not aware of this requirement.
For a development workflow with firmware using a SoftDevice, no extra steps are required.
PyOCD will by default scan flash sectors when programming flash in order to only erase and program sectors whose contents are changing. Since normally the SoftDevice sectors do not change during development, pyOCD will skip over these sectors.
In addition, a chip erased performed with a SoftDevice in flash will erase only the non-SoftDevice sectors. For example,
running pyocd erase --chip
on such a device will leave the SoftDevice intact and erase all other sectors.
However, any case where the SoftDevice sectors are being erased requires a prior mass erase. This includes changing the SoftDevice variant or version, as well as switching to firmware that doesn't include a SoftDevice. Mass erase is a separate operation. It mostly functions like a chip erase, but can also be used to [unlock]({% link _docs/security.md %}) devices that have APPROTECT enabled.
To perform a mass erase:
pyocd erase --mass
Here's some documentation that I'm writing to cover this in a new "Target family notes" section to be added to the docs.
Hey @flit is it possible to add "Short Story" and "LongStory" sections approach in the docs? Short Story just contains list of points to make things work (most people just need that). Long Story would contain your description for people that want to understand how/why things (does not) work. I found this approach useful myself when returning to a project after some time and just need a quick reminder how to make it work :nerd_face:
That's a great idea! Thanks 😄
Okay, so i can confirm that pyocd erase --chip
doesn't work but pyocd erase --mass
does work 🎉
Is "chip erase" the same a sector erase, but going through all sectors? And mass erase is a full "erase all"? That caught me a bit by surprise, I would have expect them to be the other way around.
If that's the case pyocd load
should probably accept "mass erase" as well?
$ pyocd load --help
...
load options:
-e {auto,chip,sector}, --erase {auto,chip,sector}
Choose flash erase method. Default is sector.
...
$ pyocd load --erase mass CALL_ME_MICROBIT.hex --verbose
...
pyocd load: error: argument -e/--erase: invalid choice: 'mass' (choose from 'auto', 'chip', 'sector')
Chip erase is usually a specific operation that is optimised compared to iterating over and erasing all sectors. On the nRF52, there is a chip erase, I believe. But when a SoftDevice is present, it only erases the non-SoftDevice sectors. It's also quite slow, which leads me to believe that it's erasing individual sectors. Seems faster when there is no SoftDevice present.
Mass erase is a generic pyocd term for the kind of erase that unlocks device security and/or restores the device to factory conditions. What it actually does is device-specific. For most devices, mass erase == chip erase. Pyocd only supports mass erase for Kinetis and nRF devices.
I'm hesitant to add mass erase as an additional erase type for flash programming, since it can have non-standard, device-specific behaviour. It's also something you normally don't need except in rare cases. Switching from SoftDevice to non-SoftDevice firmware is probably pretty rare. And because the SoftDevice is preserved by chip erase, and pyocd's skipping of unchanged sectors, you don't need mass erase for typical development. But it's something I'll keep in mind.
If the nRF52 chip erase goes via the flash algos, then it looks like it's using the same ERASEALL command as the mass erase:
The main difference being that the Flash algo uses the NVMC controller and PyOCD uses the CTRL AP, but I was under the impression that these two are essentially running the same operation via two different methods? So I'm a bit surprised there is a difference.
I don't think SoftDevice uses APPROTECT, as that is mostly for debug (and UICR) access and even if it's enabled it doesn't stop NVMC flash pages erases.
One thing to note is that the nRF52820/833/840 have a APL flash protection mechanism, instead of BPROT that some of the other nRF52 parts have. The BPROT can be disabled via the debugger, but having a quick look at the datasheet I didn't find a similar feature in the APL. However, both of these protections only last until the next reset (so the target code has to set these bits on every startup), so I would think that a reset and halt should be sufficient to set the target in an unprotected state?
Using the PyOCD commander I tried to do a reset halt
followed by a erase 0 1
, but that still threw the same error.
Interestingly when running just the erase
command to erase everything, no errors were shown, but the softdevice flash regions were untouched.
Switching from SoftDevice to non-SoftDevice firmware is probably pretty rare.
For micro:bit V1 is actually pretty common, as MakeCode hex files have SoftDevice and MicroPython doesn't. However for V2 both environments pack the SoftDevice.
To be fair, if I had read the PyOCD documentation (and the terminal help more carefully) I would have found the mass erase option, but looking back I wasn't the first one to make this mistake: https://github.com/pyocd/pyOCD/issues/1212
For those interested and mentioned in #1212, another non-command line workaround is to drag-and-drop the user.hex or user.bin onto the microbit drive, which will also remove the SD.
Switching from SoftDevice to non-SoftDevice firmware is probably pretty rare.
For micro:bit V1 is actually pretty common, as MakeCode hex files have SoftDevice and MicroPython doesn't. However for V2 both environments pack the SoftDevice.
Indeed, there are actually a few scenario's I ran into last year where this one-time workaround is needed in the classroom.
1) A factory new V2 with out-of-box hex (https://microbit.org/get-started/user-guide/out-of-box-experience/) is loaded with a SD. Bare metal programming using another language than MakeCode (ie. we use ADA and Arduino/C++) throws the mentioned error when trying to flash with pyocd. 2) We loan our Microbits to students for use during different courses. Each course has different project requirements so for some full wireless communication using a BLE stack is preferred and other times the radio stack with minimal flash footprint (and thus a switch) is sufficient.
The seamless workflow with the V1's without workarounds is definitely preferred! Many thanks (also from my future students :D)
Intel Hex file used for testing, but should be the same with any other: CALL_ME_MICROBIT.hex.zip
micro:bit V2.0 with the factory 0255 release (the "
Firmware for micro:bit V2
" button): https://microbit.org/get-started/user-guide/firmware/Chip erase works, but the flashing via
load
orflash
commands, with and without the--target nrf52833
flag fail:Running the sector erase command directly doesn't seem to be doing anything:
Is the error code
0x67
(103
) from DAPLink or PyOCD? That value is too large for this DAPLink enum: https://github.com/microbit-foundation/DAPLink-microbit/blob/7839c6cef09eb1b3ae66e180450753e237559bd7/source/daplink/error.h#L31-L87