I got some requests for instructions on how to flash the firmware on a kw41z-mini and this is long overdue. It flashes just like any other modern jtag/swd microcontroller that has good OpenOCD support, which is to say that the open tools we want to use are generally working well with the latest cortexm-whatever stuff we want to work with and life is pretty good right now. But if you haven't played in the 32-bit world yet then you may not have encountered OpenOCD yet, and it can take a lot of googling to figure out where to start with that. Here's a practical introduction, and of course, the oneliners you're looking for.
In this case I'm compiling RIOT-OS firmware and flashing it onto a board with a kw41z microcontroller, but this is how you could go about flashing any firmware onto any microcontroller supported by OpenOCD, which seems to be about everything in the cortexm world where all of the cool new IoT chips are.
I've been using raspberry pis in place of jtag adapters for a while and I've been incredibly happy with this setup. It's super convenient and I'm surprised more people aren't talking about how great it is. OpenOCD added support for using raspberry pi gpio pins as a jtag interface a while ago, so as long as OpenOCD supports the chip you're using then really all you need for a jtag interface is a raspberry pi. And it's like having a jtag interface on steroids that runs linux with builtin wifi and gdb and you can even compile your firmware on it if you want to. Seriously, how cool is that?
You might need the latest version of OpenOCD if your target microcontroller is a recent chip. Today my target is a kw41z which I think isn't supported in the older version available in the raspbian repos. Here are the steps to fetch and compile v0.10.0. I chose to compile only the rpi gpio driver in case it reduced compile time. I think it compiled in about an hour on an rpi 1.2b. I don't remember how long it took on a newer pi but I expect it's much faster with more than one cpu core.
git clone --depth 1 https://git.code.sf.net/p/openocd/code openocd
apt-get install libtool automake
./bootstrap && ./configure --enable-bcm2835gpio --disable-jlink && time make && sudo make install
that's it; now we flash
Now you just need to know which gpio pins you've connected to your target's SWD_CLK and SWD_IO pins, as well as which configuration file to use for your type of raspberry pi and which file to use for your target microcontroller. In this case I'm using
raspberrypi-native.cfg for an rpi 1.2b and
k40.cfg for a kw41z. Here's the command I ended up with. This inputs an elf file and flashes and runs it.
SWDCLK_PIN=2 SWDIO_PIN=3 ; openocd -f interface/raspberrypi-native.cfg -c 'transport select swd' -c 'reset_config none separate' -c 'bcm2835gpio_swd_nums $SWDCLK_PIN $SWDIO_PIN' -f target/k40.cfg -c 'init; reset halt; program /path/to/firmware.elf verify; reset run; exit'
So yeah, a raspberry pi as a jtag interface. It's really that easy. But we can take this a lot further now since our jtag adapter can do all the linux things that a linux thing can do. Let's get on with the good stuff, like gdb debugging and supercondensed oneliners.
send locally compiled firmware to a remote pi for flashing
Presumably your pi is on a network where you can reach it. This is handy since you might prefer to do your compiling on something faster than a pi and then send the binary to the pi for flashing. Here's my handy oneliner for sending an elf over ssh and telling openocd to flash it. I couldn't manage to get openocd to accept the elf directly to stdin, so I'm just using cat to drop it in /tmp where openocd can find it. I'm also using
pv to get a progress indicator for transferring the elf which is nice on slower links. If you don't want to bother with it, just replace
cat. Also replace
jtagpi.local with the address for your pi.
SWDCLK_PIN=2 SWDIO_PIN=3 ; pv /path/to/firmware.elf | ssh email@example.com "cat > /tmp/tmp.elf && openocd -f interface/raspberrypi-native.cfg -c 'transport select swd' -c 'reset_config none separate' -c 'bcm2835gpio_swd_nums $SWDCLK_PIN $SWDIO_PIN' -f target/k40.cfg -c 'init; reset halt; program /tmp/tmp.elf verify; reset run; exit'"
Ok so that's great. But can we debug remotely with gdb too? Goodness yes we can. This is where it gets good. And it's the works-out-of-the-box-automagically kind of good.
run gdb locally, connected to OpenOCD on a remote pi
Ideally we'd like to keep our source code on our local machine and run gdb there so that we get a nice responsive interface. OpenOCD will let us run it through gdb if we set up the options right. I'm disabling the tcl and telnet interfaces so that multiple openocd instances on the same pi don't block each other as I'm not using either interface. The telnet interface is useful actually, but you can instead have gdb pass commands through by prefixing them with
monitor (e.g. type
monitor reset run into gdb instead of typing
reset run into openocd's telnet interface).
SWDCLK_PIN=2 SWDIO_PIN=3 ; arm-none-eabi-gdb-py /path/to/firmware.elf --eval-command="target remote | ssh firstname.lastname@example.org \"openocd -c 'gdb_port pipe; tcl_port disabled; telnet_port disabled' -f interface/raspberrypi-native.cfg -c 'transport select swd' -c 'reset_config none separate' -c 'bcm2835gpio_swd_nums $SWDCLK_PIN $SWDIO_PIN' -f target/k40.cfg -c init\" "
That runs the arm-none-eabi version of gdb locally, telling it to ssh to a pi and run openocd there. OpenOCD is told to communicate with gdb over pipes through ssh, and since gdb is running on the same machine that compiled the firmware, it will find all the source files without any further configuration so all the backtraces and source code viewing work perfectly.
The nice thing about having gdb run openocd is first of all that it is perfectly happy to do it over ssh, which means that gdb is running on the local machine while openocd is running on the pi and we only needed one command to do it. But it also means that openocd gets started automatically when you run gdb, and it gets killed automatically when you quit gdb. I've also made sure not to add any further commands after
init so that we don't disturb the state of the target in case we want to attach to it without, well, disturbing the state of the target.
the one oneliner
This is my main oneliner for compiling a RIOT firmware and sending it to an arbitrary board attached to a pi somewhere. It's especially handy when I have various boards running various RIOT firmwares connected to various pis or perhaps all on one pi. This sets up the gpio pins as well as which RIOT example to compile and which board to compile it for, then compiles it and sends it to a pi for flashing. This is run from RIOT's root directory.
SWDCLK_PIN=2 SWDIO_PIN=3 SRC=examples/gnrc_networking ; export BOARD=kw41z-mini ; make -j4 -C $SRC && pv $SRC/bin/$BOARD/*.elf | ssh email@example.com "cat > \"/tmp/$BOARD $PINS.elf\" && openocd -f interface/raspberrypi-native.cfg -c 'transport select swd' -c 'reset_config none separate' -c 'bcm2835gpio_swd_nums $SWDCLK_PIN $SWDIO_PIN' -f target/k40.cfg -c 'init; reset halt; program \"/tmp/$BOARD $PINS.elf\" verify; reset run; exit'"
what about the reset pin?
You can use the reset pin, and you may need to if your target has the jtag hardware powered down when you try to attach to it. In that case, the reset pin is the only way openocd can signal the target to power up its jtag hardware. Lately I've committed to not connecting the reset pin on anything so that I become familiar with when it's truly required versus not, so none of these commands use a reset pin. I think you just need to change
-c 'reset_config none separate' to
-c 'reset_config srst_only srst_open_drain' and add
-c 'bcm2835gpio_srst_num $RESET_PIN' but I should come back and confirm that next time I use a reset pin.
why even bother with the 10-pin jtag headers anymore?
Good question. I'm thinking I won't put that header on anything anymore. I end up using an adapter that converts the header to .1" header pins but it's bulky and I only have two, so then I'm cutting up my good jumper wires and soldering them to the header pads and the whole thing is just silly compared to a couple of .1" pins in place of the 10-pin jtag header and they take up less space anyway.
ok but I actually did want to flash RIOT onto a kw41z-mini
Oh yeah here's the rest of that. I think we covered everything except actually producing a RIOT firmware binary. Assuming you're on some kind of modern linux (in fact you can do this 100% on a pi if you're in need of development box from scratch) all you need is to install the arm-none-eabi version of gcc/gdb and fetch RIOT from github. Here's how to do that on a pi.
apt-get install gcc-arm-none-eabi gdb-arm-none-eabi git
git clone https://github.com/RIOT-OS/RIOT.git
Most of what you git is RIOT and its drivers and included libraries and such. The stuff in examples/ is application code. Each directory in here is a thing that you can compile and flash onto a board. You can copy and modify one of those to start your application. A good starting point in general is the gnrc_networking example, or if you're on the branch with kw41z-mini support you can snoop on my telnet_shell example, which is more of a sandbox I've been playing in, but there you can see some less pristine examples of what you might expect it to look like as you start hacking an example to do what you need. That branch also has some awful hacks to the kernel that are required for telnet to work.
From RIOT's root directory you can
BOARD=kw41z-mini make -j4 -C examples/gnrc_networking to build a given example for a given board. You'll find the compiled firmware in a bin/ directory inside the given example directory. Also you can export your board and cd into an example directory and then the command to compile is shorter.
In either case the firmware ends up at