2015-04-25

Declaring Independence

In the last article, we finally took the build into our own hands. There was still a third-party library we relied on, however. No longer! Today is the day we're declaring independence and finally manage to run a program on the Arduino Due that solely consists of our own code.

Before I knew how to achieve that, I needed to find out what exactly that third-party library (libsam_sam3x8e_gcc_rel.a) was doing. The only clue I had to go on was the entry point of our binary, Reset_Handler.

Looking at the contents of the library[1], I was able to find out that multiple object files had been linked together to form that library. Reset_Handler originally came from a file named startup_sam3xa.o. All the sections coming from other object files looked, by their names, like functions for accessing the hardware. Since I knew I was using none of those, I guessed that those other object files were irrelevant and could be ignored.

I also guessed that the Arduino repository would have the source code for the library. A bit of digging turned up five different files named startup_sam3xa.c. Some detective work then helped figure out which one was the right one:

That left a single file, which in addition contained comments referring to Arduino, so that one looked pretty good. What I did next was copy that file and all the header files it included into my repository. I then went on to cut out as much of that code as I could, without breaking the program.

Eventually I was able to integrate what remained into blink.c. Let's look at what the file looks like now, part by part:

// blink.c

// This is the top of the stack, as provided to us by the linker.
extern unsigned int _estack;


// This is a partial definition of the vector table. It only defines the
// first two entries which, as far as I can tell, are the minimum needed
// for a program to work at all.
// Space for the other interrupt handlers is reserved. I'm not sure if this
// is necessary, but I can imagine that the vector table not having the
// right length could cause all kinds of problems (imagine if it was too
// short, and the linker would place something else directly after it).
typedef struct {
	void *initial_stack_pointer_value;
	void *reset_handler;

	char other_interrupt_vectors[44 * 4]; // space for 44 32-bit pointers
} VectorTable;


void start();


// The vector table. We're using GCC-specific functionality to place this
// into the .vectors section, not where it would normally go (I suppose
// .rodata). The linker script makes sure that the .vectors section is at
// the right place.
__attribute__ ((section(".vectors")))
const VectorTable vector_table = {
	(void *)(&_estack),
	(void *)start,
};
			

Here we set up the vector table. When various things happen, for example an external I/O signal, or the system being reset, this can lead to an interrupt. An interrupt, as the name suggests, interrupts the normal execution of code and calls a handler function for that specific case.

As you can see, I cheated as much as I could get away with. Instead of defining all 46 entries of the vector table[2], I only defined the two I absolutely needed to get the program running: The initial value of the stack pointer[3] and the reset handler.

// blink.c, continued

// Addresses of several registers used to control parallel I/O.
static volatile int * const pb_pio_enable          = (int *)0x400E1000;
static volatile int * const pb_output_enable       = (int *)0x400E1010;
static volatile int * const pb_set_output_data     = (int *)0x400E1030;
static volatile int * const pb_clear_output_data   = (int *)0x400E1034;

// Bit mask for PB27. This is pin 13 (the built-in LED) on the Arduino Due.
static const int pb27_mask = 0x08000000;

// Addresses of several registers used to control the real-time timer.
static volatile int * const timer_mode_register  = (int *)0x400E1A30;
static volatile int * const timer_value_register = (int *)0x400E1A38;
			

This is about the same as before, with one crucial difference: Previously, those have been global variables. Now they are constants. The difference is important, as global variables end up in a section of the executable called .data, while constants go into .rodata. The contents of the .data section require some special handling, which I've omitted for now[4].

// blink.c, continued

// As the name suggests, this function sleeps for a given number of
// milliseconds. Our replacement for Arduino's delay function.
void sleep_ms(int milliseconds) {
	int sleep_until = *timer_value_register + milliseconds;
	while (*timer_value_register < sleep_until) {}
}

// This function is the entry point for our application and the handler
// function for the reset interrupt.
void start() {
	// Enable PB27 (pin 13) and configure it for output.
	*pb_pio_enable    = pb27_mask;
	*pb_output_enable = pb27_mask;

	// Set the timer to a resolution of a millisecond.
	*timer_mode_register = 0x00000020;

	// Continuously set and clear output on PB27 (pin 13). This blinks
	// the Due's built-in LED, which is the single purpose of this
	// program.
	while (1) {
		*pb_set_output_data = pb27_mask;
		sleep_ms(200);
		*pb_clear_output_data = pb27_mask;
		sleep_ms(800);
	}
}
			

This part stayed almost the same, except that I've renamed main to start, which I like better.

There's a very important thing to note here: As I've mentioned above, I've omitted some very important pieces of initialization code. We happen to get away with this, because this program doesn't use any global variables. I've added two extensive comments to the full version of the file, so check those out if you're interested in the details of my reasoning.

That's it! This program can be compiled and uploaded to the Arduino Due with the following commands:

arm-none-eabi-gcc \
	-nostdlib \
	-mcpu=cortex-m3 \
	-mthumb \
	-Tlinker-script.ld \
	-Wl,--entry=start \
	blink.c \
	-o blink.elf

arm-none-eabi-objcopy \
	-O binary \
	blink.elf \
	blink.bin

bossac --write --verify --boot -R blink.bin
			

Please don't forget that you have to press the ERASE button on the Arduino Due before you can upload the program.

There's one nice detail I want to mention before closing up this article. Here's what bossac printed when we first used it to upload the program:

Device found on ttyACM3
Write 11952 bytes to flash
[==============================] 100% (47/47 pages)
Verify 11952 bytes of flash
[==============================] 100% (47/47 pages)
Verify successful
Set boot flash true
CPU reset.
			

And here's the output from uploading today's program:

Device found on ttyACM3
Write 352 bytes to flash
[==============================] 100% (2/2 pages)
Verify 352 bytes of flash
[==============================] 100% (2/2 pages)
Verify successful
Set boot flash true
CPU reset.
			

Yes, you're reading that right. Even though the program does the same as it did before, blinking the LED, the size has been reduced by 97%! Of course, it will all go downhill from here. The program is bound to grow again, as we build up our own abstractions to access the hardware.

That's it for today. As always, the full code is available on GitHub. See you next time!