Fun with a chip from Texas Instruments [part 1]
I've found this really cool chip. It's from Texas Instruments and it's called the MSP430. It's got a 16-bit RISC CPU inside of it, capable of different power modes (including ultralow power, down to 0.7µA at 1MHz!). You can even power this thing off a couple of AA batteries. You can get a development kit called the Launchpad for a subsidized price of approx $5 USD.
I run OS X (the same applies if you run Linux) so I could not run the Windows-based IDE that comes with the MSP chip. That's fine, because in this case I prefer my command-line tools anyway. In fact, to learn as much as possible about this chip, I wanted to get down to the bare metal and code some assembly!
So what do we need? Well, we need an assembler, a linker and a device programmer tool. I'm going to be using the GNU assembler and linker, along with mspdebug. (Use the links mentioned to get started).
Secondly, we need the official specs and datasheets in order to learn as much as possible about the CPU. The most important one is the user manual, which I've uploaded here.
Now let's begin. We first need to tell the linker about the memory map for the executable that we are going to produce. Looking at the MSP's memory map, we can see that code memory begins at 0xF800 and we are given 2kB of this code space. The interrupt vector table begins at 0xFFE0, and is exactly 32 bytes. There are different MSP chips with slight differences, e.g. the G2031 only has 512 bytes of code memory. The best thing is to have a look at the datasheet for the chip you'll be using (look for "Memory Organization"). I'm using the MSPG2231 chip.
Let's call this file test.map:
MEMORY
{
TEXT : ORIGIN = 0xf800, LENGTH = 0x0800
DATA : ORIGIN = 0x1000, LENGTH = 0x0100
VECTORS : ORIGIN = 0xffe0, LENGTH = 0x0020
}
SECTIONS
{
.text : { *(.text) } > TEXT
.data : { *(.data) } > DATA
.vectors : { *(.section .vectors) } > VECTORS
.bss : { *(.bss) }
}
With this map file we're basically telling the linker that when it produces the final ELF executable, the target device needs to put the code data on 0xF800 (2kB space available), the interrupt vectors on 0xFFE0 and so on. Don't worry about the .bss section and the .data section for now, as in our first example we won't be using them.
Now, let's proceed with our first test program! Call this file test.asm.
.global main
.set WDTCTL, 0x0120 ; watchdog control port
.set WDTPW, 0x5A00 ; watchdog power
.set WDTHOLD, 0x0080 ; watchdog hold
.set P1DIR, 0x0022 ; direction of data bits on port P1
.text
main:
mov #0x0280, r1 ; set stackpointer to top of RAM
mov #WDTPW+WDTHOLD, &WDTCTL ; stop watchdog
bis.b #0x41, &P1DIR ; lights up red LED and also green LED
jmp $+0 ; we are done
.section .vectors, "a" ; Set attribs CONTENTS, ALLOC, LOAD, READONLY, DATA
.word 0x00, 0x00, 0x00, 0x00
.word 0x00, 0x00, 0x00, 0x00
.word 0x00, 0x00, 0x00, 0x00
.word 0x00, 0x00, 0x00, main
Note that with the GNU assembler, any section that is not .text, .data, .bss, and so on, has to be added with ".section" (or .sect), this is unlike other assemblers.
Let's start with the bottom section - the .vectors section - just to get it out of the way. As you may remember, the interrupt vector table is 32 bytes (it is in fact 64 bytes on my chip, but I don't need to worry about the additional 32 bytes right now). The only vector I need is the RESET vector, and you'll see that I've pointed the last word (2 bytes) to main's address! Without this, you won't get very far. Also note that the section has got the "a" attribute. This is just so that the linker will include the .vectors section in the ELF executable that we'll be producing.
Let's have a look at the main procedure. Setting the stack pointer (SP is the same as the R1 register) at the top of RAM is a part of the initialization. We also stop the watchdog, although this code would have worked without it. The "BIS" instruction sets Port 1.0 (red LED) Port 1.6 (green LED) to ON, so that they are lit.
Let's translate the assembly code to object code!
$ msp430-as -o test.o -mmsp430x2012 test.asm
Now we have test.o, although it's an ELF executable, it won't run on the target device. We'll need to run the linker on it, referring to our memory map file (test.map).
$ msp430-ld -o test.elf -T test.map test.o
Great. Now we've got the ELF executable, which we will upload and run this using the 'mspdebug' tool, as follows:
$ mspdebug rf2500 "prog test.elf"
[..snip snip.]
Programming...
Writing 18 bytes to f800...
Writing 32 bytes to ffe0...
$
Voila, we got the end result. Both the green LED as well as the red one are now lit. A very primitive program indeed, but at least now we've sorted out how to use the GNU assembler/linker along with mspdebug to code assembly for the MSP430 chips. In the next article we'll do something more interesting!