Bare Metal on the NXT

By Sivan Toledo
May 2008

If you ever wished to program the NXT at the lowest and most powerful level, but did not want (or could not) deal with SAM-BA, the native boot loader, then this project is just what you were looking for: a way to invoke bare-metal programs on the NXT from the standard firmware. Well, almost the standard firmware: a slightly modified version that John Hansen developed for this project.

If you are the developer of a programming environment that normally gets installed on the bare metal, this project would be useful for you too. It would allow users to invoke your programming environment from the (almost) standard firmware.

This project was developed in collaboration with John Hansen, who modified the NXT's standard firmware. Thanks John!

Introduction

There are now many ways to program the NXT. You can use the graphical NXT-G, or LabView, or NXC, all of which use the standard firmware. You just boot your NXT, download a program, and run it from the menu system. Programming environments that control the NXT from a program running on a PC via USB or Bluetooth also use the standard firmware.

Then there are programming environments that control the NXT's processor directly, without using the standard firmware: LeJOS, pbLua, nxtOSEK, NXTMOTE, NXTGCC, and NxOS. (RobotC uses a modified version of the standard firmware, so it is somewhere in between.) To use these, you need to replace the standard firmware with a special firmware. In some cases, you can use the NXT's software to download the special firmware, but in other cases you must use SAM-BA, the native boot loader of the NXT's processor. I never managed to get SAM-BA to work under Windows Vista, so this method was blocked for me.

What I wanted was a way to program the NXT at what is called the bare-metal level, with total control of the NXT's processor and hardware. But I wanted these programs to be invoked from the standard firmware, so I can download them to the NXT using standard tools (like NXT-G or nexttool), and so that I can run a bare-metal program, shut it down, and switch to running a NXT-G or NXC program without downloading anything, as long as all the program were already stored on the NXT.

John Hansen, who has already hacked the standard firmware and produced slightly modified versions, came to my rescue. He and I designed a simple interface that allows a slightly modified firmware to recognize an rxe file as a bare-metal binary and to jump to code stored in it. Once control passes to the bare-metal binary, it has complete control of the NXT. If it is very careful it may be able to return control to the NXT's firmware, but normally it overwrites memory and reconfigures the NXT's hardware, so it can't return to the firmware. When the program is done, it can either shut down the NXT or reset it. In both cases, you get back to the standard firmware.

I wrote a little package that shows up how to use this concept. The most important components of this package are an assembly-language startup code, which is called by John's almost-standard firmware and a simple linker script that produces a binary program in the correct format and layout. The startup code initializes the NXT, pretty much as if it was just reset, and calls a function, c_startup. This function can be written in C (this is how I wrote it), and has complete control of the NXT.

The package also includes a number of simple device drivers, which were mostly adapted from the NxOS project. Currently (May 2008), these cover raw analog sensing, simple motor control (including rotation to a given angle), input from the NXT's buttons, displaying text on the LCD, and playing sounds. I will also appreciate bug fixes.

Why?

This project has two goals. The primary goal of the project is to allow native programming of the NXT while using firmware that can also run NXT-G programs and other programs that use the standard firmware. This also implies that you can use standard tools to download native programs to the NXT.

Why write native programs? Mostly, because it allows you to experiment with embedded programming at the lowest and most powerful level. You can try to run the NXT's processor at a slow clock rate, to save power (although the NXT is not really suitable for low-power applications, since its auxiliary processor, the AVR, only enters low-power mode after turning off the main processor); You can experiment with programming the NXT to emulate a variety of USB peripherals, from a mouse to a keyboard to a speaker, using the built-in USB support in your PC's operating system; You can write programs that perform very fast sensing or actuation, to control infra-red remote-controlled devices, or to record speech; You can use a standard C or C++ compiler, rather than almost-standard C languages. I have not tried all of these, but in principle they should all be possible.

The other motivation for this project is to allow developers of alternative firmware to create versions of their firmware that can be invoked by the standard firmware. That is, I am hoping that the developers of nxtOSEK, for example, would create a version of it that would allow users to build a nxtOSEK program, download it to a NXT running John's almost-standard firmware, and run it. This would eliminate the need to replace the firmware before running any nxtOSEK program. I used nxtOSEK as a mere example here; I hope that there would be similar setups for LeJOS, NXTMOTE, and other firmware.

Running these alternative firmwares under the setup that John and I created does place one limitation: we run the native programs entirely from RAM, so the alternative firmware and whatever application it is running must fit in the NXT's 64 KB of RAM. In principle it should be possible to extend our setup to run programs directly from the file in the NXT's file system, but this would require a much more complex loader than we have now. I hope that the setup can support some alternative firmware even with this limitation.

Producing a new NXT firmware for native programs was not one of my goals. There are several reasons for that. First, there are already such projects, including nxtOSEK and NxOS. Second, I believe that there is significant learning value in having users experiment with device drivers rather than use existing sophisticated drivers. Leaving the package minimal promotes user experimentation. Having said that, I will appreciate contributions from others and will incorporate them if their style is consistent with that of the package.

How?

Let's start with the firmware's perspective. The firmware inspects the first few bytes of all rxefiles. If the file starts with "MindstormsNXT", the firmware runs it normally. John modified the firmware such that if the file starts with "NXTBINARY", the firmware reads a 32-bit integer from location 12 in the file, and then calls a function stored in that location inside the file. The firmware passes to this function two arguments: the address of the file in memory (NXT files are stored contiguously in the processor's flash memory, so they have a memory address), and the length of the file. That's all the firmware does.

The function that the firmware is written in assembly. This function disables interrupts and then copies the entire file, except for the first 16 bytes, to the beginning of RAM, which starts at address 0x200000. Then it jumps to the beginning of RAM, which contains a jump to the rest of the initialization code. Now the code sets up the stacks, zeros the uninitialized static variables, resets all the on-chip peripherals of the processors. Then it sets up a memory mapping that mirrors the beginning of RAM starting at address zero (the low area of memory contains the interrupt vectors on ARM processors), and calls a function c_startup, which can be coded in C.

At this point, the processor is at the same state after being powered up (since we reset all the on-chip peripherals), but the rest of the NXT might not be in its initial state. If the standard firmware set up the Bluetooth module and the LCD, then they remain in their current state until the native program resets them. The processor is now running very slowly, using an internal oscillator that runs at around 32 kHz. Interrupts are disabled.

If you plan to adapt these structure to some existing firmware, it helps to understand the structure of the binary file. It starts with 12 bytes that contain the string "NXTBINARY" and padded with zeros. Then the offset of the relocation routine in the file, 48 in my code. This offset is computed automatically by subtracting two addresses, so the code should work even if you reserve 64 bytes for the vectors (which some firmwares do).  Now come the reset, exception, and interrupt vectors, which are eight relative branch instructions. These will be copied to the beginning of RAM and mapped to the beginning of the address space. The eight instructions take 32 bytes, so the relocation function, which follows, is at offset 48 (or 16+64 if you reserve more space for the vectors). The linker script is very simple, putting the code and data sections contiguously starting at an address that is 16 bytes before RAM starts. Therefore, the linker puts the vectors directly at the beginning of RAM.

What?

Even small embedded programs need some structure and some conventions. I tried to impose as little structure as possible, to allow users to use the code in many different program structures: with a preemptive kernel, like OSEK or eCos, or with just an infinite main loop, etc. But I had to make some assumptions. This section lists them and explains the structure of the code.

I used a number of conventions.

Finally, some potential pitfalls.

Getting Started

It's pretty easy to get started. You need two tools: the GNU development tools for ARM processors, and some way to download the resulting programs to the NXT. I use nexttool to download programs to the NXT, but I suppose that you could use NXT-G as well. There are several distributions of the GNU tools for ARM around. I usually use a distribution called WinARM, which is for windows; GNUARM and YAGARTO are two other options (I have used GNUARM on Linux).

You will also need John Hansen's modified firmware (John tells me that any version numbered 106 or later includes the native-invocation feature), and a copy of my source code. My code is most likely still buggy, so have a Lego antenna at the ready, to reset the NXT (or any other stick that can press the reset button).

I may still tweak (and fix) the code, so the interfaces may not be completely stable in the near future. But the firmware/native interface will be stable.

Good luck!

© 2007, Sivan Toledo