What CortexProg can do


Summary

CortexProg is a debugger for Cortex-M microcontrollers. It can serve as the only tool needed to reverse-engineer devices, create new designs, debug prototypes, program boards on production lines, or general for-fun hacking on devboards. CortexProg can read data from a microcontroller, write data into it, program flash, provide live tracing for printf-style debugging (ZeroWireTrace), and even allow complete GDB debugging. The PC-side tool uses the HID transport to not need any drivers on any of the supported OSs: Linux, Windows, MacOS. The tool source is also available, so you can build yourself a copy for whatever other esoteric environment you might desire to run it on. CortexProg even supports multi-core chips!


Command Line

CortexProg utility takes its parameters on the command line. Each operation to be performed is listed in the order they are to be done. Each command takes the form of a command word followed by zero or more parameters. Some commands have optional parameters. If such a parameter is not given, the documented default behaviour takes place. Some commands take parameters that are numbers. these can be provided in decimal, just written as a number, or in hexadecimal, written preceded by 0x. There is one special parameter to the CortexProg utility that does not follow these rules. It is the serial number parameter. If it is provided, it must be first.


Pins

CortexProg v4 pinout

CortexProg's built-in level shifters allow attaching to any target whose signal voltage is between 0.5V and 3.6V. The SWDIO, SWCLK, UART RX, and UART TX signals all have level shifters built-in to allow this. The VCC/Vref pin is used as the reference voltage for the level shifters. This means that you should either tell CortexProg to supply power (see the power command) or connect VCC/Vref to the target's VCC so that level shifters can work properly.

CortexProg's designed to have through-hole 4x1 2.54mm/0.1inch header attached for the debug interface and a 2x1 2.54mm/0.1inch header attached for the UART, but you may choose to instead solder wires or alligator clips there instead.


The commands


help

CortexProg help
CortexProg help <COMMAND>
$ CortexProg help
USAGE: CortexProg [command [param1 [param2 [...]]]] [command2 [...]]
Commands are executed in the order they are given.
KNOWN COMMANDS:
  core
  debug
  erase
  eraseall
  fwupdate
  help
  info
  power
  read
  special
  speed
  trace
  upload
  write
You may learn more about any command by trying:
  CortexProg help 
$
$ CortexProg help trace
USAGE: CortexProg trace trace_addr
	ZeroWireTrace the running application. Use the given
	address as the mailbox. Any writeable word in RAM or
	MMIO will do. Anytime during tracing, pressing [ENTER]
	will stop the tracing. Note that this may block the
	debugged program

The help command shows help. With no parameters, it will just show the basic CortexProg help. If a parameter is given, is it assumed to be a command whose help you'd like to see. For example CortexProg help write will show the help for the write command.


info

CortexProg info
$ CortexProg info
CortexProg tool ver 1.2.0.1 (c) 2017 CortexProg.com
SWD HW: 'ARM CortexProg' ver. 1, serial '24b6bb03556dcc82'
SWD FW: 1.2.0.0, maxXfer: 1032, flags 0x00000060:
  flag 0x00000020: Supports clock speed control (10.500000 MHz max)
  flag 0x00000040: Supports multi-core targets
  Cortex-M3 found
   CORE ID 0x0100
   ROMTABLE base 0xe00ff003
periphId = 0x00000000000a0410
JEDEC = {0, 0x20, 0x410}
Chip is by 'STMicroelectronics', model 0x410
CPUID = {0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000010, 0x00000004, 0x0000000a, 0x00000000, }
CPUID/script options found: 1:
  STM32F1xx @ '/usr/share/cortexprog/scripts/STM32F1xx.20000000.swds'
 trying script file '/usr/share/cortexprog/scripts/STM32F1xx.20000000.swds' (212 bytes to 0x20000000)
  script uploaded
 CPU identified as 'STM32F1xx', will use scriptfile STM32F1xx.20000000.swds
 loaded at 0x20000000. Staging area will be 0x20000400
running stage 1 init...
running stage 2 init...
running stage 3 init...
Supported ops: ERASEALL ERASEBLOCK WRITEBLOCK
Chip has 1 flash area (128KB total):
   128KB at 0x08000000 ( 128 x 1KB)

The info command puts CortexProg into verbose mode and then tries to identify the debugger, the attached chip, and the attached chip's memory layout. All of this information will be printed on the console as a result of this command.


power

CortexProg power 2500 <OTHER COMMANDS> power off
CortexProg power on <OTHER COMMANDS> power off
$ CortexProg power on read dump.bin 0 65536 power off
  READING 00000000..0000FFFF..0000FFFF 100% [====================]  36.178 KB/s

The power command controls CorterProg's handling of the Vtarget rail, that is to say that it controls whether CortexProg supplies power to the target and at what voltage. Depending on the CortexProg hardware version, different power options are available. The initial AVR-based hardware, and the modern hardware versions 0 and 1 support no power control. Version 2 hardware support on/off control and always supplies 3.3V, up to 50mA. Version 3 hardware can supply up to 100mA at a variable voltage between 700 and 2500 mV, in 10 mV steps. The latest hardware (version 4) can supply up to 250mA at a variable voltage between 700 and 3300 mV, in 10 mV steps. Version 4 is the only hardware version that is publicly available, so unless you were a beta tester of CortexProg, only that part applies. As far as parameters go, the value given is the desired voltage. If the current debugger cannot supply it, an error will be shown. The word "off" can be used as a synonym for "0" and "on" for "3300" - 3.3V.


speed

CortexProg speed 1000000 <OTHER COMMANDS>
$ CortexProg speed 10500000 read dump.bin 0 65536
  READING 00000000..0000FFFF..0000FFFF 100% [====================]  35.814 KB/s
$ CortexProg speed 1000000 read dump.bin 0 65536
  READING 00000000..0000FFFF..0000FFFF 100% [====================]  25.539 KB/s
$ CortexProg speed 100000 read dump.bin 0 65536
  READING 00000000..0000FFFF..0000FFFF 100% [====================]   6.520 KB/s
$ CortexProg speed 10000 read dump.bin 0 65536
  READING 00000000..0000FFFF..0000FFFF 100% [====================]   0.772 KB/s

The speed command controls CorterProg's SWD bus clock speed, that is to say that it controls how fast the SWD bus runs. Generally CortexProg will try to run the bus at the maximum speed it is capable of. However, in some cases, such as certain low power microcontrollers, there can be a need to run at a lower speed temporarily. This command's parameter is the desired clock speed in Hz. All publicly-released versions of CortexProg support this functionality, but technically it is optional in CortexProg. Current version of CortexProg's minimum clock speed is near 1.3 KHz. The current maximum clock speed is around 10.5 MHz.


read

CortexProg read <filename>
CortexProg read <filename> <start address> <length>
$ CortexProg read full_flash_dump.bin
  READING 08000000..0801FFFF..0801FFFF 100% [====================]  35.320 KB/s
$ CortexProg read partial_flash_dump.bin 8192 1024
  READING 00002000..000023FF..000023FF 100% [====================]  33.333 KB/s

The read command reads memory from the target device into a file. Any accessible memory area may be read, including ram, flash, and I/O registers. If no address and length are provided, CortexProg will read the largest (or the only) flash memory bank in the device. This will only work if there exists a chip support script for this device, as without it, CortexProg will not know where to find the flash and how much to read.


upload

CortexProg upload <filename> <start address>
CortexProg upload <filename> <start address> <length>
$ CortexProg upload small_upload_to_ram.bin 0x20000000 1024
UPLOADING 20000000..200003FF..200003FF 100% [====================]  90.909 KB/s
$ CortexProg upload larger_upload_to_ram.bin 0x20000000
UPLOADING 20000000..20004FD0..20004FD0 100% [====================]  90.201 KB/s

The upload command writes memory to the target device from a file. Any accessible memory area that may be directly written by on-device code, including ram, and I/O registers can be used as a target of this command. Thus, this command does not write flash. If no length value is provided, CortexProg will read the file length and upload that much. If a length is provided, that many bytes are uploaded. If the file is larger, the first bytes are uploaded. If the file is smaller, zeroes are written to pad to the requested length.


eraseall

CortexProg eraseall
$ CortexProg eraseall
Performing "erase all"...
 done

The eraseall command performs the "Erase All" operation on the chip. This requires a chip support script which supports the "Erase All" operation. The precise definition of what gets erased by this operation depends on the specific target device. For most chips this erases all of flash. In some chips, some parts of flash remain un-erased, like for example calibration data or serial numbers and MAC addresses.


erase

CortexProg erase
CortexProg erase <start address> <length>
$ CortexProg erase 0x08001000 8192
  ERASING 08001000..08002FFF..08002FFF 100% [====================]  14.109 KB/s
$ CortexProg erase
  ERASING 08000000..0801FFFF..0801FFFF 100% [====================]  14.259 KB/s

The erase command performs one or more "Erase" operations on the chip. This requires a chip support script which supports the "Erase" operation. The precise definition of what can be erased by this operation depends on the specific target device. For most chips this erases one or more blocks of flash. If no address and length are provided, the largest flash area is erased, one block at a time. If address and length are provided, that requested area is erased. Since erase has to erase a complete block, if you provide an impossible address or length, an error will be shown, suggesting a correction.


write

CortexProg write <filename>
CortexProg write <filename> <start address>
CortexProg write <filename> <start address> <length>
CortexProg write <filename> noerase
CortexProg write <filename> noerase <start address>
CortexProg write <filename> noerase <start address> <length>
$ CortexProg write binary_data.bin
  ERASING 08000000..080053FF..080053FF 100% [====================]  14.502 KB/s
     DONE 08000000..080053FF..080053FF 100% [====================]  11.229 KB/s
$ CortexProg write binary_data.bin 0x08010000
  ERASING 08010000..080153FF..080153FF 100% [====================]  14.413 KB/s
     DONE 08010000..080153FF..080153FF 100% [====================]  11.200 KB/s
$ CortexProg write binary_data.bin 0x08010000 1024
  ERASING 08010000..080103FF..080103FF 100% [====================]  14.286 KB/s
     DONE 08010000..080103FF..080103FF 100% [====================]  11.363 KB/s
$ CortexProg write binary_data.binnoerase 0x08010000 1024
     DONE 08010000..080103FF..080103FF 100% [====================]  15.384 KB/s

The write command performs one or more "Write" operations on the chip. This requires a chip support script which supports the "Write" operation. The precise definition of what can be written by this operation depends on the specific target device. For most chips this write one or more blocks of flash. If no address and length are provided, the largest flash area is written to. If an address is provided but no length, the length of the input file is used, rounded up to the next write block size. If a length is provided, that many bytes are written. If that does not line up with a write block size, an error is shown. If the file is smaller than the requested, zeroes are written up to the requested size. If the file is larger than the requested write, only the requested length is written. Generally, the write command will pre-erase the area. If it is already erased, the noerase parameter may be used to skip the pre-erase step.


trace

CortexProg trace <address>
$ CortexProg write dictaphone.bin trace 0x20004ffc
 write: automatically rounding write up by 428 bytes (to 0x00000800)
  ERASING 08000000..080007FF..080007FF 100% [====================]  14.286 KB/s
     DONE 08000000..080007FF..080007FF 100% [====================]  11.428 KB/s
TRACING using mailbox at [0x20004FFC]. Press [ENTER] to terminate
configuring!
hello, world from code in the C-M3!

The trace command starts ZeroWireTrace using the given address as a mailbox address. The tracing will continue displaying trace data until you press [ENTER]. There is more about ZeroWireTrace later in this document.


debug

CortexProg debug <port number>
CortexProg debug noreset <port number>
Console #1
$ CortexProg debug 12345
 debug: GDB server listening on port 12345. Press [ENTER] to close connection
 debug: accepted a connection
 debug: found support for 4 watchpoints: R W RW. Max size: 32768b
 debug: found FBPv1 with support for 6 breakpoints and REMAP support

 debug: key press detected. Closing connection.
Console #2
$ arm-none-eabi-gdb dictaphone.elf 
GNU gdb (GNU Tools for ARM Embedded Processors) 7.4.1.20121207-cvs
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=i686-linux-gnu --target=arm-none-eabi".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from dictaphone.elf...done.
(gdb) target rem:12345
Remote debugging using :12345
0x080001a6 in ADC1_2_IRQHandler () at main.c:174
174		mBuf[mBufPos] = audioSamp / 16 - 4096;
(gdb) info regi
r0             0x6f	111
r1             0x20000000	536870912
r2             0x7ff	2047
r3             0x196	406
r4             0xa	10
r5             0x0	0
r6             0x8000610	134219280
r7             0x20000	131072
r8             0x20000106	536871174
r9             0x20000000	536870912
r10            0x1	1
r11            0x720a6c13	1913285651
r12            0x12	18
sp             0x20004f40	0x20004f40
lr             0xfffffff9	4294967289
pc             0x80001a7	0x80001a7 <ADC1_2_IRQHandler+19>
cpsr           0x21000022	553648162
(gdb)

The debug command starts a GDB server which you can attach to for live debugging of any code running in the target device. The parameter given is the port number to use. To attach, open GDB and give it the following command: target rem :<port number>. Generally, CortexProg will reset the target before attaching. The noreset parameter can be used to prevent that reset, allowing attaching live to currently-running code.


special

CortexProg special <script name>
CortexProg special <script name> "{script params here if any}"
$ CortexProg special stm32f1_write_option_bytes
 special: assuming you meant 'SPECIAL/stm32f1_write_option_bytes.lua' by 'stm32f1_write_option_bytes'
This special function needs parameters. Eg:
	read <ADDRESS>
	erase
	write <ADDRESS> <VALUE>
$
$ CortexProg special stm32f1_write_option_bytes "{read 0}"
 special: assuming you meant 'SPECIAL/stm32f1_write_option_bytes.lua' by 'stm32f1_write_option_bytes'
 OPTION BYTE offset 0x00 has a value of 0xA5 (complement matches)
$
$ CortexProg special stm32f1_write_option_bytes "{erase}"
 special: assuming you meant 'SPECIAL/stm32f1_write_option_bytes.lua' by 'stm32f1_write_option_bytes'
 - FLASH_SR=0x00000000
 - FLASH_SR=0x00000000
 - FLASH_SR=0x00000000
 - FLASH_SR=0x00000000
 unlocking flash
 unlocking option byte access
 - FLASH_CR=0x00000200
 erasing option bytes...
  waiting for completion
 - FLASH_SR=0x00000800
 - FLASH_SR=0x00000800
 - FLASH_SR=0x00000800
 - FLASH_SR=0x00000800
 - FLASH_CR=0x00000260
$
$ CortexProg special stm32f1_write_option_bytes "{write 0 0xA5"}
 special: assuming you meant 'SPECIAL/stm32f1_write_option_bytes.lua' by 'stm32f1_write_option_bytes'
 - FLASH_SR=0x00000000
 - FLASH_SR=0x00000000
 - FLASH_SR=0x00000000
 - FLASH_SR=0x00000000
 unlocking flash
 unlocking option byte access
 - FLASH_CR=0x00000200
 writing option byte 0 with value 0xA5...
  saving value to R1
  saving location to R0
  writing instruction to RAM
  reading instruction from RAM
  setting SR.T
  setting PC
  verifying PC
  stepping
  verifying post-step PC
  waiting for completion
 - FLASH_SR=0x0001F800
 - FLASH_SR=0x0001F800
 - FLASH_SR=0x0001F800
 - FLASH_SR=0x0001F800
 - FLASH_CR=0x00000210

The special command starts a LUA interpreter and allows it full access to the debugger and the target. This can be used to implement all sorts of functionality including unlocking locked chips, factory flashing, and reverse-engineering. The parameters that will be passed to this script must be enclosed in double quotes and then curly braces exactly as shown above. There is more written about special scripts later in this document. If the script name parameter is a path, CortexProg will not search further. If it ist just a name, CortexProg will search a few directories for a matching special script (name you gave with a ".lua" extension). The search is done, in order, in: the SPECIAL subdirectory in the current directory, next whatever path is stored in the CORTEXPROG_SPECIAL_DIR environment variable, then /usr/share/cortexprog/special, and then the current directory. This means that the scripts that come with CortexProg can be referred to simply by their name with no extension. For example, you can unlock a locked Nordic nRF51 chip, the command CortexProg special nrf51_unlock.


fwupdate

CortexProg fwupdate <firmware file>
$ CortexProg fwupdate fw_1_2_0_1.bin
BL 'ModulaR BL!' v2 (proto v1). Flash: 0x4000 - 0x10000 with (768 x 64-byte blocks) <ENCRYPTION REQUIRED>
Applying 11520-byte update (180 blocks)
  ERASING 00004000..0000FFFF..0000FFFF 100% [====================]  48.854 KB/s
  WRITING 00004000..0000FFFF..0000FFFF 100% [====================]  85.561 KB/s
upload done - telling device
rebooting device - please wait
CortexProg will now exit

The fwupdate command allows you to update the firmware of your CortexProg device. Firmware updates can bring fixes or new functionality. You can get new firmware files on the downloads page. Make sure to use the correct firmware file for your device.


core

CortexProg core 0x0001
$ #read CPUID on core with ID 0x0001
$ CortexProg core 0x0001 read /dev/stdout 0xe000ed00 4 | od -tx4
  READING E000ED00..E000ED03..E000ED03 100% [====================]   1.953 KB/s
0000000 410cc601
0000004
$ #read CPUID on core with ID 0x0302
$ CortexProg core 0x0302 read /dev/stdout 0xe000ed00 4 | od -tx4
  READING E000ED00..E000ED03..E000ED03 100% [====================]   1.301 KB/s
0000000 410fc241
0000004

The core command allows you to select which core of a multi-core chip to attach to for debugging and all other requests you make of CortexProg. This command will not do anything for single-core chips. The parameter is a COREID value. To see what the IDs and types of cores are in your target chip, you can use the command CortexProg info.


the "serial number" parameter

CortexProg -s
CortexProg -s <debugger serial number> <all other commands>
$ CortexProg read /dev/stdout
More than one device found. Available 2 device(s):
	'24b6bb03556dcadc'
	'24b6bb03556dca4f'

$ CortexProg -s
 Available 2 device(s):
	'24b6bb03556dcadc'
	'24b6bb03556dca4f'

$ CortexProg -s 24b6bb03556dcadc info
CortexProg tool ver 1.2.0.2 (c) 2017 CortexProg.com
SWD HW: 'ARM CortexProg' ver. 1, serial '24b6bb03556dcadc'
SWD FW: 1.2.0.1, maxXfer: 1032, flags 0x00000060:
  flag 0x00000020: Supports clock speed control (10.500000 MHz max)
  flag 0x00000040: Supports multi-core targets
  Cortex-M3 found
   CORE ID 0x0100
   ROMTABLE base 0xe00ff003
periphId = 0x00000000000a0410
JEDEC = {0, 0x20, 0x410}
Chip is by 'STMicroelectronics', model 0x410
CPUID = {0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000010, 0x00000004, 0x0000000a, 0x00000000, }
CPUID/script options found: 1:
  STM32F1xx @ 'SCRIPTS/STM32F1xx.20000000.swds'
 trying script file 'SCRIPTS/STM32F1xx.20000000.swds' (212 bytes to 0x20000000)
  script uploaded
 CPU identified as 'STM32F1xx', will use scriptfile STM32F1xx.20000000.swds
 loaded at 0x20000000. Staging area will be 0x20000400
running stage 1 init...
running stage 2 init...
running stage 3 init...
Supported ops: ERASEALL ERASEBLOCK WRITEBLOCK
Chip has 1 flash area (128KB total):
   128KB at 0x08000000 ( 128 x 1KB)

This must be the first parameter given to CortexProg. If no further parameters are given, CortexProg will list all the available devices by serial number. If more parameters are given, the very next is considered the serial number of the debugger to use. This is used to support multiple debuggers attached to the same machine. If only one is attached, CortexProg will simply use it, if the -s flag is not given.


Chip Support Scripts


What a Chip Support Script Is

A chip support script is a special file that CortexProg uses to support erasing, writing, and better identifying particular chips. The exist in a particular folder, where CortexProg will look for them. You may add more anytime and they will be used when appropriate. First, the SCRIPTS directory in the current directory is searched. Next, if the environment variable CORTEXPROG_SCRIPTS_DIR is set, the directory it names is searched. Next, /usr/share/cortexprog/scripts is searched, and then the current directory is.

The chip support script contains identifying information about a chip or a chip family, instructions on how to identify the chip definitively, information on how to erase and write flash in the chip, and descriptions on the memory layout of the chip's flash.


Where to get Chip Support Scripts

Many chip support scripts come with CortexProg, and you can expect more to be available her eon this site. You can see the currently-supported devices here. If a device you'd like to see supported is not, you can always email us a request or Read the developer info section to learn how to make one.


Special Scripts


What a Special Script Is

A special script is a LUA script that CortexProg PC utility runs. Such a script has complete access to the debugger, target device, and the flash-writing script. This is actually quite a powerful thing. The LUA script can do anything from the very low level SWD transaction (like finding custom APs), to mid-level things like memory I/O, and even to the highest-level things like using the chip support script 's abilities to write and erase flash. The special script can take arbitrary parameters, and do anything a full LUA script can do, including file I/O. Most interestingly, it can do all of these things without stopping the code running in the debugged device.


What a Special Script Can Do

A special script can be used for such things as unlocking code-readout-protection chips. In many cases this requires access to a special AP or a special memory location. In many cases normal reads and writes will not work (since even such things as identifying the chip may fail). In this kind of a situation, a special script can be used. An example of chip unlocking is the efm32hg_unlock script, which unlocks using a custom AP. Another example is the nrf51_unlock script, which unlocks using memory access. A special script can also be used to implement writing custom non-flash values. An example of such a special script is the stm32f1_write_option_bytes script which allows reading and writing option bytes on the STM32F1 chip family. Another very good use case of a special script is factory flashing of a device. Sometimes special steps beyond just writing flash are needed, like assigning custom serial numbers or MAC addresses. A special script can be easily made to do this. One can also use a special script to watch an executing program's actions live, since it can read memory and perform complex tasks without stopping the target device. Another cool example was the recently-famous PSoC4 reverse-engineering effort. Special scripts were developed for each part of that effort to dump the secure ROM from the PSoC4.


Where to get Special Scripts

You may get special scripts as part of the CortexProg archive or here on this site. You may also write such scripts yourself. The developer info section of this site explains how to write such a script, and the examples that ship with CortexProg can be used as a reference.


ZeroWireTrace

One of CortexProg's coolest functions is ZeroWireTrace. With ZeroWireTrace, you can easily do printf-style logging and/or debugging without needing to use a serial port or some other method of data output. It also requires no extra wires and guarantees that no data is ever lost (writes are blocking until the logs are received). On the device-side you simply need to wire up your putchar() function to the ZeroWireTrace one. It should look like this:

void zwtPutchar(char ch) {
	volatile uint32_t *mailbox = (voilatile uint32_t*)MAILBOX_ADDRESS;
	while (mailbox[0] >> 31);	//wait for previous char to be read
	mailbox[0] = 0x80000000ul + ch;
}

You should then define MAILBOX_ADDRESS to be some address that you can read and write to in the microcontroller's ram or registers. This can be just a global uint32_t type variable, an I/O register in the chip that you aren't not using, or any other place you can write. Due to how GCC usually generates startup code, the last word in RAM is usually safe to use for this. Trace output will be printed on the console in the PC-side tool. If no debugger is connected, the application will never proceed past the first print. This is convenient to make sure that you never miss any logged output. The parameter to the trace command is, of course the value of MAILBOX_ADDRESS.


Built-in CDC UART

CortexProg v2 and later include a build-in CDC UART. This means that using minicom, HyperTerminal, or any other similar program you can use CortexProg as a serial port to connect to your project. All standard baudrates are supported; even, odd, and no parity options exist; stop bit options are: 1, 1.5, and 2 stop bits; 4 - 8 bits per character are supported. Using the built-in CDC UART does not require use of the CortexProg PC-side tool - this functionality is entirely independent of the other functions of CortexProg and only exists in CortexProg since it was widely requested during the beta program.

© 2017-2018