Monday, August 31, 2009

msploader

It was quite difficult (for me) to come up with the right google searches, but I finally found not one but two efforts to loading the msp430 from linux, specifically the boards designed for the ez430 using the usb stick that comes with the ez430. The problem is the usb interface is done with a separate processor which has its own protocol on the usb serial side and from that creates the spy-bi-wire on the target msp430 side. TI did not publish this protocol but instead hid it in a windows dll. So the linux solution thus far has been to use some tricks to ride on top of that windows dll. One was called msp430fet by Travis Goodspeed (if not every then every other google search you do will hit his blog). He stopped working on it when fetproxy came out which is an attempt to replace msp430-gdbproxy on linux without the windows dll.

My goals are much much simpler. I want a program to load a binary from a file into the msp430. Finding the libraries and building and using gdb with fetproxy is more than I am willing to tolerate.

I was already working on a disassembler for the msp430. Not for public consumption really but I feel the best way to learn an instruction set is to write a disassembler for it. Elf files are somewhat trivial to read, esp for finding a single chunk of binary data, so naturally I started there. The disassembler is still in the package below, no guarantees that it is accurate, for accurate or at least supported disassembly use binutils objdump.

The current release of msploader is here:
http://www.dwelch.com/msp430stuff/msploader-r001_36d6295d5849.tar.gz

Prior releases:
http://www.dwelch.com/msp430stuff/msploader-r000_923b533b2beb.tar.gz

It happens to be a full hg repo with change info. Although the initial release, rev000 has only the one commit of the initial source.

This is a somewhat brute force program. For now the serial port is hardcoded, you will need to go into ser.c and change it. I will probably fix that before long and make it a command line option. Also, it is assuming a single binary with a single entry point, basically no interrupt support or any other events in the interrupt vector table at this time. The code is simple enough though that adding support for multiple blocks is not hard. At this time I do not have support for intel hex files, only elf files as I use binutils to generate my binaries and leave them in the native elf format without the extra objcopy to intel hex. For this tool to be useful to other compilers and toolchains I should add support for intel hex files.

One thing I learned fairly quickly was not to perform an erase all. The problem is that it erases all of the flash. The program space and ivt are fine, but it also clears the configuration space with the factory calibrated clock initializaition values. msploader erases flash pages as needed to load the program and leaves the rest unchanged. The ivt does have to be cleared in order to program the entry point.

At the time of this writing there are some Linux problems with the ez430 usb stick. Running on ubuntu 9.10 beta (9.10 wont be out for a month or two) and 9.04 you have the problem that you can only open the serial port one time per reboot of the computer. I think I saw a website with a solution but have not persued it. My choice was instead to downgrade to 8.10. 8.10 has its own problems but with little effort you can be in a situation where you can load the msp more than once without having to reboot the computer.

When you plug the usb stick in you will see something like this:

[85098.925011] ti_usb_3410_5052: TI USB 3410/5052 Serial Driver v0.9
[85099.064020] usb 4-3: new full speed USB device using ohci_hcd and address 5
[85099.280621] usb 4-3: configuration #1 chosen from 2 choices
[85099.283545] ti_usb_3410_5052 4-3:1.0: TI USB 3410 1 port adapter converter detected
[85099.283684] ti_usb_3410_5052: probe of 4-3:1.0 failed with error -5


Notice how usb 4-3 is mentioned in the above output, your output will vary, but with 8.04 or 8.10 should end in the error -5.

Using the usb information from dmesg perform this step:

echo 2 > /sys/bus/usb/devices/4-3/bConfigurationValue

This results in a new dmesg output:

[85295.502696] ti_usb_3410_5052 4-3:2.0: TI USB 3410 1 port adapter converter detected
[85295.502963] usb 4-3: TI USB 3410 1 port adapter converter now attached to ttyUSB2


The specific usb port will also vary so for now you need to examine the output of dmesg and change ser.c to match (and re-compile) before using msploader.

So long as you do not remove the usb stick you can load the msp430 as many times as you like.

This msploader release also includes an openmsp430 directory. This will be described in a separate post. Basically using an open source msp430 core in verilog and icarus verilog to create a simulation you can execute your msp430 programs with visibility inside the pins. I dont expect this to be a 100% accurate at the signal level core, but you can certainly watch the rom, ram and registers, something you cannot do staring at the chip on a board.

llvm and clang for the msp430

As of this writing the stable llvm release does not contain msp430 support, but versions in svn do so I am using the following steps to build llvm using clang as the c frontend.

My instructions are derived from the clang instructions on this page:
http://clang.llvm.org/get_started.html

I tried using trunk for a while but it did not take long before I synced with a release that wouldnt build. I did not want to be that cutting edge, so by wandering around starting at these web addresses:
http://llvm.org/svn/llvm-project/llvm/ for llvm itself and
http://llvm.org/svn/llvm-project/cfe/ for clang I found what they called release_26 in both repos. ymmv.


svn co http://llvm.org/svn/llvm-project/llvm/branches/release_26/
cd release_26
cd tools
svn co http://llvm.org/svn/llvm-project/cfe/branches/release_26/ clang
./configure --enable-targets=msp430 --enable-optimized -disable-doxygen --prefix=/llvm
cd /path/to/release_26/
make
make install


As with binutils --prefix=/llvm defines the installation directory for the binaries, you do not need to build in or anywhere near that directory, nor do you need to prep that directory, make install takes care of it. It does need to be a path where you have file permissions to create and write files.

Note, the build time for llvm is quite slow, not sure why. If you have a multi core processor you can speed it up by using the -j option on make, for example a four core machine you might try

make -j 3

By default all targets are enabled, by using --enable-targets and limiting it to only the targets you are interested in will greatly reduce the compile time.

Now for a test program:


unsigned short add_them ( unsigned short a, unsigned short b )
{
return(a+b);
}


I named mine test2.c


PATH=/llvm/bin:$PATH
clang -Wall -emit-llvm -c test2.c -o test2.bc
opt -std-compile-opts test2.bc -f -o=test2opt.bc
llc -march=msp430 test2.bc -f -o=test2.s
llc -march=msp430 test2opt.bc -f -o=test2opt.s


I wanted to show a blinking led example using a timed counter, but of course the optimizer removes the counter loop, so I changed it to the above simple example that optimizes to what you would expect.

Not optimized:


cat test2.s

.file "test2.bc"


.text
.align 4
.globl add_them
.type add_them,@function
add_them:
.BB1_0: # %entry
sub.w #6, r1
mov.w r15, 2(r1)
mov.w r14, @r1
mov.w r14, r15
add.w 2(r1), r15
mov.w r15, 4(r1)
add.w #6, r1
ret
.size add_them, .-add_them


Optimized:


cat test2opt.s

.file "test2opt.bc"


.text
.align 4
.globl add_them
.type add_them,@function
add_them:
.BB1_0: # %entry
add.w r14, r15
ret
.size add_them, .-add_them



What llvm gives you that gcc does not is several optimization options.

With gcc your optimization is limited per function on the initial compile step.

With llvm you can optimize on
-the initial compile step.
-per bytecode file (not limited to per function per file but the whole file)
-link individual bytecode files to one bytecode file and optimize that one bigger file
-optimize on the llc step from bytecode to the target processor

Linking individual files to one file and then optimizing that file is done like this:

llvm-link a.bc b.bc -f -o=ab.bc
opt -std-compile-opts ab.bc -f -o=abopt.bc



Even if you limited yourself to two optimization levels, none and full, the number of optimization combinations still adds up to more than you are going to be willing to try.


I did one optimization experiment using zlib to compress and deflate some text and then compared the output to the original. For that one experiment optimizing on the initial c compilation and/or optimizing the individual bytecode files hurt the overall performance. The best results were when I compiled from c to bytecode files with no optimizations, then linked the bytecode files into one big bytecode file, then I performed the optimization on the one big bytecode file. This kinda makes sense as the optimizer has the most amount of code to work with. That doesnt mean there are other combinations that would have performed better I did not try them (would have been a few hundred tests).

llc has optimization on by default and I leave that as is since the bytecode doesnt know the target platform only llc can take advantage of the processors features and instructions.

Note for that one test on that one day for that one processor gcc produced a little bit faster code, around 10% if I remember right. llvm is being actively developed and from the google searches sounds like it is the standard compiler for iPhone development so I have faith that those numbers will get better with time (and with other programs/algorithms that I did not test).

There is also a gcc for the msp430, I do not have any specific objections to that toolchain, it is quite popular from what I can tell for the msp430 family. Binaries are available, build instructions involve taking a certain gcc and patching it.

With the world going 64 bit I recently ran into problems with the gcc frontend to llvm. This was not on the msp430 but the ARM (take the build instructions above and change --target=msp430 to --target=arm,msp430 and your one build turns into a multiprocessor cross compiler, I think if you leave the --target off you get all the targets!). llvm-gcc with or without -m32 caused longs to be 64 bit on a 64 bit linux and 32 bit on a 32 bit linux. For cross compiling the hosts processors interpretation of long should not affect the results. Granted loose use of the long variable type is the problem of the programmer not the compiler. zlib has longs and ints scattered willy-nilly, something I hope will get cleaned up in the near future. So my choices were 1) fixup zlib, 2) only use 32 bit linux installs 3) switch from llvm-gcc to (the immature clang) and use -m32. I chose the latter.

binutils for msp430

binutils currently supports the msp430 family of microcontrollers, it is quite simple to build your own cross binutils from sources.

Start by downloading the binutils sources
http://ftp.gnu.org/gnu/binutils/

As of this writing 2.19.1 is the current release, for a direct link
http://ftp.gnu.org/gnu/binutils/binutils-2.19.1.tar.gz

Navigate to a place where you want to perform the build, this does not need to be the final destination you can remove the source directory after you finish the build and install.

The --prefix=/msp430 configuration option specifies the path where I want the binaries installed once compiled. You do not need to prepare this path ahead of time, the make install step will do this, it does need to be a place where you have file permissions to create and write files.

These steps normall work with mingw32 and msys on windows, at this time I have only tried it on Linux (32 and 64 bit ubuntu 8.04 through 9.10beta)


tar -xzvf binutils-2.19.1.tar.gz
cd binutils-2.19.1
./configure --target=msp430 --prefix=/msp430
make
make install


For ubuntu you may need packages like build-essential, bison, flex and possibly others.

To test your install, create a simple program like this one:


outer:
mov #1234,r15
inner:
dec r15
jnz inner

jmp outer


I named mine test.s


PATH=/msp430/bin:$PATH
msp430-as test.s -o test.o
msp430-ld test.o -o test.elf
msp430-objdump -D test.elf

test.elf: file format elf32-msp430


Disassembly of section .text:

0000fc00 <__ctors_end>:
fc00: 3f 40 d2 04 mov #1234, r15 ;#0x04d2

0000fc04 :
fc04: 1f 83 dec r15 ;
fc06: fe 23 jnz $-2 ;abs 0xfc04
fc08: fb 3f jmp $-8 ;abs 0xfc00



Looks like it is working. The device I am using, the 2012, a starting address of 0xFC00 is fine, you can change this by adding -Ttext 0xFD00 to the msp430-ld command (0xFD00 or whatever address).
My loader writes the reset address in the vector table to match the starting address in the binary (.elf) so I dont worry about making binutils do that. Your program will not run though unless address 0xFFFE in flash contains the entry point address of your program.

getting started

I have had an interest in the TI msp430 microcontroller for a few years now. Became aware of it when the ez430 came out (http://www.ti.com/ez430), I am a sucker for $20 microcontroller evaluation boards. I really like the instruction set, the size, power, cost, etc. I want to remember that I became frustrated for a couple of reasons, one most likely was lack of linux support for the tool so I had to use windows. Perhaps even at the time the windows tools, I am not interested in an IDE, in fact will intentionally avoid them. I want my text editor a command line compiler and a dumb loader. I want to remember that I didnt like something about being forced to use the internal RC based clock.

Recently I dusted off the ez430 box and started playing with it again. Started to think this instruction set is so simple perhaps I can make an emulator. Stumbled on two linux based attempts at loaders. Found that an msp430 backend has begun for llvm. All of this combined has re-sparked an interest. And most recently I gave up on the simulator I had started as I found (at least one) an open source core in verilog that runs nicely under icarus verilog (http://www.icarus.com/eda/verilog).

So the next few posts will cover building binutils from sources so that you have an assembler and linker. The instruction set is so simple why would you want to use C? If you do though I have a build of llvm working using llvm's clang C frontend (the gcc front is undesireable as a cross compiler on 64 bit systems, at least for ARM, so I prefer the clang front end for now as the results are consistent between 32 and 64 bit Linux). And then a simple loader program for the ez430 based usb stick. And on top of all that using the openmsp430 core for simulating programs and getting much better visibility for debugging.