of Southern Appalachia
The first installment of this series described a method of accessing the EZ430's MSP430 firmware by way of JTAG, while the second installment took a detour to discuss the TUSB3410 usb controller of the board. This third installment is concerned with the analysis of the green EZ430U firmware; that is to say, it is concerned with the firmware of the six-pin EZ430f2013 kit, which might differ from the four-pin EZ430f2013 kit and drastically differs from the red EZ430rf2500 kit.
Section 1, wherein we make general, even visual observations regarding the organization of firmware, as well as the locations of important objects.
Observe the following memory map, which was generated by the .memmap.gd.xview macro of msp430static.
In an m4s memory map, the X axis represent the less-significant byte while the Y axis represents the more significant byte. 0xFF00 would be the top left, and 0xFFFF would be the top right. As the interrupt vector table (IVT) of the MSP430f1612 ranges from 0xFFE0 to 0xFFFF, it appears as a green band in the upper right of the image. Entries within it all point to the upper red band, with addresses varying from 0xfb78 to 0xfdb0, in regular increments of 4. The only exception to this rule is the RESET vector at 0xfffe, which points to 0xfdaa.
Why such an arrangement? Each of these regularly-spaced interrupt handlers is a branch to a lower interrupt handler. The blue line that you see at the top of the black expanse is a second IVT, entries of which are called from above. 0xffe0, the DMA vector, point to a branch to &0xf7e0, which is to say that address which is contained within 0xf7e0. Similarly, 0xffe2 points to a branch to 0xf7e2. In fact, every interrupt except for RESET points to nothing but a branch to its equivalent in the table that begins 0xf7e0.
It should be clear, then, that not one but two programs reside in this image. The first-stage firmware, which rests at the top of memory, performs its own initialization when the chip is reset, but differs all interrupt handling to the lower firmware, which grows from the beginning of flash to 0xf7e0. It shouldn't be hard to instruct a compiler to build a second-stage firmware compatible with the first-stage, but it would be imprudent to spring into such a task without at least a casual analysis of the first stage.
Section 2, wherein we examine the first-stage firmware in depth.
The first-stage firmware is a bootloader which resides from 0xf800 to 0xffff in flash memory of an MSP430F1612. A quick search with MSP430static--which I'll refer to as 'm4s' for the sake of brevity--reveals that 30 functions have been found in this area. Fifteen of these, those in the range [0xfb78,0xfbb0], are merely branches to interrupt handlers in lower memory. The interrupt handler is a function which I'll call init() that resides at 0xfdaa, calling a config() function at 0xf828 to set registers to their proper values. The only call made by config() is to delay() at 0xf812 that uses nested loops to implement a timing delay.
I/O is performed by putbyte() and getbyte() functions at 0xfbc6 and 0xfbd2 respectively. These use USART0 in UART mode. This port is configured, as are many other peripherals, in the previously mentioned config() function.
As the purpose of this series of articles is to describe the process for writing a complete replacement firmware, I should mention that such information is to be found within this stage. Port 3 is especially important, as it is tied into both UARTS and I2C. As you'll recall from the early articles of this series, the TUSB3410 refuses to load with ports in the default configuration. By matching the configuration of the original firmware, we ought to be able to get something going.
To that end, Port 3 is configured as follows within the config() function.
f88e: bis.b #48, &P3SEL
f894: bis.b #16, &P3OUT
f89a: bis.b #16, &P3DIR
Clocks and such must also be reconfigured, but I come bearing good news! There's an easier way, in that by relocating the IVT or reconfiguring your compiler, you can generate a custom second stage firmware without rewriting the first stage.
Section 3, wherein we examine the second-stage firmware in brief.
By comparison to the simple, neighborly firmware of the preceding section, the second stage firmware is gigantic and multi-tasking, with a rats-nest of function calls to a few key functions. Although it serves little immediate value, I'll cover second stage as an example of a few analysis techniques.
Which function calls are most popular, and what do they do? The .calls.top10 macro results in the following:
163 4c3e 4c3e
151 4f54 4f54
142 4b6a 4b6a
29 4eca 4eca
28 6100 6100
27 2ba2 2ba2
24 8d0e 8d0e
24 a784 a784
20 5f4c 5f4c
17 756e 756e
Note the sharp dropoff after the third. In a multi-tasking application, such as a TinyOS wireless sensor node, it would be logical to assume these to be mutex locks. In a JTAG adapter, however, it is much more likely that these serve an I/O purpose. Sure enough, all three are I/O related with 0x4f54 performing I/O through function calls and the other two doing it directly.
As I am not concerned with reverse-engineering the TI's proprietary debugging protocol, but rather only with making software capable of running on the EZ430 programmer, I'll delve no further into the second-stage firmware. Remember only the following: (1) That the IVT of the second-stage code resides at 0xf7e0, (2) that all else remains unchanged.
The question then becomes one of relocating the IVT, which may be accomplished either by altering linker scripts or by rewriting the firmware image before flashing it to the device. We shall begin with the prior method.
Section 4, wherein we rewrite the linker scripts of two popular compilers so as to generate our own second-stage code for this fine platform.
Moving the IVT is accomplished in GCC by forking msp430x1612.x to ez430u.x, then changing line 8 to the following:
vectors (rw) : ORIGIN = 0xf7e0, LENGTH = 0x20Call GCC with the -T switch followed by your forked linker script, and the output will direct to the proper address. Use msp430-objdump to verify the address in your output. For further details, consult my article on Retargetting the MSPGCC Linker. In the case of the IAR compiler, the IVT range is defined near the end of lnk430F1612.xcl.
Please note that, for no reason that I can fathom, the RESET vector of the second-stage IVT has been hard-coded to be 0x2502 in some places. You must set the .text section to begin at 0x2502, or the reset will land in the middle of a two-word instruction.
Section 5, wherein your author makes a shameless plug his upcoming appearance at the tenth annual Toorcon San Diego but provides little else of substance.
A fourth installment of this series will wrap up the replacement MSP430 code, concluding with a complete and commented `hello world' example for GCC.