Thu
Feb 12

A reset button for the CI20 Creator MIPS-based board


I’ve been rambling about the CI20 board a lot recently. It’s a fun board, but it lacks a hardware reset button.

Fortunately you can easily hook one up using the EJTAG header. Connect a momentary switch between RST_N (pin 11) and GND (pin 2, 4, 6, 8 or 10). If your board is angled the same way as mine is in the picture, then the pin-out of the header matches that on the hardware page (linked above) — i.e. RST_N is second from bottom on the left and GND is third from bottom on the right.

I’ve got somewhat-reassuring confirmation from the CI20 mailing list that RST_N has a debounce circuit and a pull-up resistor, so a simple switch should be okay.

Wed
Feb 11

The impact of caching on MIPS


I wrote my bare-metal CI20 demo tutorial to run in the uncached area of the MIPS address space. This means that all memory accesses bypass the cache completely and go straight to and from main memory. Unsurprisingly, this is quite slow.

It’s pretty easy to change the demo to use cached memory:

  • Fix the entry point: modify linker.lds, changing “. = 0xa8000000” to “. = 0x88000000". This moves the code from kseg1 (uncached) to kseg0 (cached);
  • Fix the stack: modify start.S, changing “li sp, 0xa9000000” to “li sp, 0x89000000”. This does the same thing to the stack;
  • Make it more obvious: modify the delay function in main.c, making the loop from 0 to 1000000 (previously it ended at 1000); and
  • Rebuild: make clean && make, then copy the new hello.bin to your tftp directory.

If you do that, then you will see the very dramatic effect that caching has on code performance on the MIPS! I made a short video (37 seconds) demonstrating the difference: https://www.youtube.com/watch?v=Ve0xtVGApbA

Tue
Feb 10

Running bare-metal code on a ci20: part 2

This is a follow-up to part 1, from yesterday.

By this point you have a serial connection to the board, and you have a toolchain. Let’s write some code!

Step 3: Code

What to code? We could always output something to the UART, but why do that when the board has a nice, large, glowing LED on it? The hardware page has a short recipe to make the led appear purple by rapidly switching it between red and blue. Let’s do that.

Ideally we’d like to write as much as possible in C, but C needs a stack, so the first thing we do is write a very short assembly language program which sets up a stack pointer and jumps to C. Create a file named start.S and add this to it:

#include "mipsregs.h"

/* make it accessible outside */
.globl _start
/* Tell binutils it's a function */
.ent _start
.text

_start:
	/* Set up a stack */
	li sp, 0xa9000000 

	/* And jump to C */
	la t0, entrypoint
	jr t0
	nop

.end _start
	  
All this file does, apart from play nice with binutils, is set up a stack pointer and jump to a symbol named “entrypoint”, which is where we’re going to write our C. Where did the stack pointer address come from? I basically made it up. :) Looking at the MIPS memory map, we know we have an uncached area, called kseg1, starting at 0xa0000000 and ending at 0xc0000000. On this board the first 256MB of that are directly mapped to RAM. We’ll use this area to store code, data, and the stack.

What about “mipsregs.h”? GCC doesn’t natively understand the symbolic register names “sp” and “t0”, so I wrote a small header which defines them for me. The important parts are these two lines:
#define t0 $8  /* temporary values */
#define sp $29 /* stack pointer */
	  

… but the full file is included in my Github repository. See below.

We now have some code which jumps to a function named “entrypoint”, so the next step is to write “entrypoint”. Create a file named main.c, and add these lines:

#define GPIO_F_SET 0xb0010544
#define GPIO_F_CLEAR 0xb0010548

#define GPIO_F_LED_PIN (1 << 15)

static inline void write_l(unsigned int addr, unsigned int val)
{
    volatile unsigned int *ptr = (unsigned int *)(addr);

    *ptr = val;
}

static void delay()
{
    volatile int i;

    for(i=0; i<1000; i++)
        ;
}

void entrypoint(void)
{
    /* Do the purple LED thing */
    while(1) {
        write_l(GPIO_F_CLEAR, GPIO_F_LED_PIN); /* Turn LED blue */
        delay();
        write_l(GPIO_F_SET, GPIO_F_LED_PIN); /* Turn LED red */
        delay();
    }

}
		


As described in the ci20 hardware page, the LED is accessible via a general-purpose IO port — GPIO — called GPIO F (the board also has GPIO ports A to E, and each port is 32 bits wide). Bit 15 of that port controls the LED. If it’s set to 0, the LED is blue, and if it’s set to 1, the LED is red.

The jz4780 makes it very easy to set and clear bits in the GPIO ports. Each port has a “set” address and a “clear” address. Any 1s which you write to the “set” address cause the corresponding bit of the GPIO port to be set, and, conversely, and 1s which you write to the “clear” address cause the corresponding bit to be cleared. In both cases, 0s are ignored. So you can just set or clear the bits you need without worrying about reading from the port first to avoid changing values you’re not interested in.

You can see that the GPIO ports lie in the uncached KSEG1 region as well. In fact, the jz4780 processor reserves the upper 256MB of KSEG1 (0xb0000000-0xbfffffff) for memory-mapped devices.

The final thing we need to do is to instruct the linker to put everything at an address inside KSEG1. Create a linker script named linker.lds and add the following:

OUTPUT_ARCH(mips)

ENTRY(_start)

SECTIONS
{
    /* Our base address */
    . = 0xa8000000;

    /* Code */
    .text : {
        *(.text)
    }

    /* Static data */
    .rodata : {
        *(.rodata)
        *(.rodata.*)
    }
    /* non-static data */
        .data : {
        *(.data*)
    }
}
Linker scripts are often referred to as voodoo, but this one is pretty simple. We tell the linker to start writing “text” (program code) at 0xa8000000, and that the first thing in it must be _start. Data comes after the code.

Step 4: Compiling

Now to build everything. Let’s use Make. Create a file named Makefile:

AS=mipsel-unknown-elf-as -mips32
CC=mipsel-unknown-elf-gcc
LD=mipsel-unknown-elf-ld
OBJCOPY=mipsel-unknown-elf-objcopy
CFLAGS=-Os

OBJS=start.o main.o

hello.bin: hello.elf
	$(OBJCOPY) -O binary $< $@

hello.elf: $(OBJS)
	$(LD) -T linker.lds -o $@ $+

%.o: %.[Sc]
	$(CC) $(CFLAGS) -c -o $@ $<

clean:
	rm -f *.o *.elf *.bin

This Makefile does a few interesting things.

  • It uses our custom-built toolchain, in which all the tools are named mipsel-unknown-elf-something
  • It uses GCC to compile everything, even the assembly language files. This is standard practise and it’s because GCC runs the C preprocessor over the file, if it ends in S, so we can use those symbolic register names.
  • It turns the output of the linker into a raw binary file using objcopy.

That last step is required because the linker will produce an “elf” file, which is a structured file in the Executable and Linkable format, the standard format for executable files on Linux (and many other Unix-like systems, but not Macs). However, we’re going to use uboot to load the file, and uboot expects the file to be in a raw “memory image” format, which it can just copy directly into RAM and run. We keep the .elf file around, though, because it’s useful for debugging.

You should now build everything:

$ make

If you get this far, you’re ready to boot your new “kernel”.

Step 5: Booting!

First, we have to set up a TFTP server. On a Mac, you can use the built-in TFTP server by running these commands from a terminal:

$ sudo launchctl load -F /System/Library/LaunchDaemons/tftp.plist
$ sudo launchctl start com.apple.tftpd

This will serve files from /private/tftpboot. Copy your new code there:

$ sudo cp hello.bin /private/tftpboot/

Now connect an Ethernet cable to your ci20, get your serial terminal ready, and reset the board. When you see "Hit any key to stop autoboot”, which happens within 5 seconds of booting, press a key. You should be greeted by a uboot prompt:

ci20#

This is uboot, which is quite a featureful bootloader — type “help” to get an idea of what it can do. For now, we’re going to configure it to load our file via tftpboot. Set the server IP (the IP address of your TFTP server) and the board’s IP address. My server is at 192.168.1.12, and I gave the board an IP of 192.168.1.7:

ci20# setenv serverip 192.168.1.12
ci20# setenv ipaddr 192.168.1.7

Now we can instruct uboot to load our file:

ci20# tftpboot 192.168.1.12:hello.bin
Load address: 0x88000000
Loading: #
         12.7 KiB/s
done
Bytes transferred = 144 (90 hex)

Note that the default load address is at 0x88000000, which isn’t what we asked for. However, it’s not a problem, because this address and 0xa8000000 both refer to the same location, but the former is cached.

If you get this far, cross your fingers, and type:

ci20# go 0xa8000000
## Starting application at 0xA8000000 …

You should get a pretty purple (well, pinkish, really) LED, with no operating system required.

Step 6: Future work

Some things one might do to expand on this:

  • Enable caches and work from cached memory
  • Write to the uart, to make a true “Hello, world!” program
  • Set up a timer and flip the LED from the timer interrupt routine
  • Read up on MIPS TLB refil and run the LED flipping function from KUSEG (also known as user space) by mapping memory in using that function
  • Use the timer to switch between executing one function which turns the LED red, and another which turns it blue…

Complete code

Is available from Github:

https://github.com/nfd/ci20-hello-world


Running bare-metal code on an imgtec ci20 MIPS-based board

The MIPS32 CI20 board from Imagination is a cheapish dual-core MIPS processor with a bunch of fun extra stuff on it. This post shows you how to start running your own code on the board without an operating system — no Linux, no nothing. We’ll use it to blink a LED really fast :), but this approach is how you would start writing a new operating system for the board, if you were so inclined. 

This is part one of a two-part tutorial — part 2 is here (and also linked below).

This post shows you a setup for a Mac. It is marginally easier to do this from Linux (and the Mac-specific parts are made obvious), and marginally harder to do it from Windows (and I have no idea how — something something cygwin).

We’ll connect the board to a serial terminal, set up a MIPS toolchain (compiler and associated programs), write the smallest possible (almost) program which does something useful, and then boot it from the board. In part 1, we’ll get the hardware and toolchain set up, and in part 2 we’ll write code and boot it.

Ready?

You will need:
  • ci20 creator board
  • A usb-to-serial connector, such as these FT232RL boards from ebay. Note that your USB to serial must support, and must be set to, 3.3 volts. Often they support both 5 volts and 3.3 volts, and can be switched from one to the other with a jumper or a small switch. 
  • A few cables to connect the usb-to-serial to the board. You’ll need 3. I used ones like these.

Some handy web pages to refer to when going through this tutorial:
Step 0: Beware of static

Static electricity can destroy things. Be careful when working around the board, particularly in low relative humidity situations (such as winter). Either ensure you periodically ground yourself (the common UK advice is to touch a water pipe), or get an antistatic wrist strap.

Step 1: Serial

First we want to be able to talk to uboot, which is the board’s bootloader. Uboot communicates over uart4, which is described in the hardware page as “Dedicated UART header”.

You’ll need to connect the TXD, RXD, and GND pins to your usb-to-serial connector. On my FTDI-based connector, these connect directly — TXD to TX, RXD to RX, and GND to GND. If you are using something else as your usb-to-serial (such as an Arduino), you may need to switch the TXD and RXD cables.

Once you have connected your wires, plug the usb-to-serial cable in. This will create a device node. On the mac, for an FTDI-based board, this will show up as /dev/tty.usbserial-XXXXXXXX, where the Xs change for different boards.

I used GNU Screen as a serial terminal. From a terminal, type

$ screen /dev/tty.usbserial-A900K06B 115200

… remembering to use the correct Xs, of course, and power up your CI20.

If all goes well, after a few seconds, you should see the CI20 load uboot. After another few seconds, you will see it load Linux. Eventually, you will get a terminal prompt and can log in (l/p ci20/ci20).

So now you have a “headless” ci20 setup, but that’s not so interesting, so shut the board down:

$ sudo halt

… and disconnect the power to the board (you can keep the usb-serial connected if you like). We’ll come back to it when we have something for it to run.

If you’re using GNU Screen, you can get back to a terminal by pressing ctrl-A k, that is, holding ctrl, pressing A, releasing both keys, and then pressing k. It will ask if you’re sure. You are. :) First stage complete!

Step 2: Toolchain

Things get a bit tedious in this step. You’re going to compile gcc and friends for MIPS. On a Mac, this involves creating a case-sensitive disk image, using crosstool-ng to compile everything, and then installing and setting up a PATH. (On Linux, it’s the same process, but you don’t have to create a disk image).

So, for the Macs: open Disk Utility and create a new disk image of at least a few GB. Make sure the image type is set to “Case sensitive”.

Mount the new image and cd to it, eg:

$ cd /Volumes/ct

Now install Crosstool-NG. On a Mac, I recommend using homebrew:

$ brew install crosstool-ng

Now we’re going to create the MIPS compilers. Start by setting up a big-endian MIPS template:

$ ct-ng mips-unknown-elf

Now modify it:

$ ct-ng menuconfig

We’re going to make two changes:

  • In “Paths and misc options”, set “Prefix directory” to something sane. I picked /usr/local/mipsel-unknown-elf. If you do this, make sure you have write access to /usr/local/.
  • In “Target options”, set “Endianness” to “Little endian” as that’s the endianness of the ci20 board.
Now select “Exit” (arrow right) until you return to your prompt. You can now build:

$ ct-ng build

This will take a long time, but will, eventually, hopefully, complete without error. When this happens cd out of your case-sensitive disk image and unmount it. You won’t need it again and can throw it away if you like.

Now, add the tools to your path. The path you want to add is “bin/“ after whatever you set as “Prefix directory” above. So in my case, I added the following to my ~/.bash_profile:

export PATH=$PATH:/usr/local/mipsel-unknown-elf/bin

Close and re-open your terminal if you changed your .bashrc, then test out your new compiler:

$ mipsel-unknown-elf-gcc
mipsel-unknown-elf-gcc: fatal error: no input files
compilation terminated.
$

If you see GCC complaining in the above way, you now have a toolchain! The hard part is over.
Fri
Jan 2

Help me beta test Divers Alarums


Over the holidays I wrote Divers Alarums, an Android alarm clock app. It’s a solve-my-own-problems type app with a fairly basic feature set: it fades in the alarm over a period of time (this is why I wrote it), supports multiple alarms, lets you set alarms for specific days of the week, has a snooze feature, displays a notification, and is reasonably clean-looking and free of obnoxious ads (by which I mean it has no ads).

If you have an Android phone and know how to install APK files, I’d really appreciate beta testers. The only thing I’d ask is that if you actually use it for your alarm clock, please set a different alarm clock as well, because I’m sure there are bugs at this point.

Please email me if you have any suggestions (no matter how small or weird) or if you find any bugs. In a couple of weeks, if it looks okay, I’ll put it on the Play store. Here’s the APK:

Divers Alarums beta (APK, 1.0M), last updated Jan 4, 2015

Thanks!

Mon
Nov 17

Graphing in JavaScript

JavaScript libraries which draw graphs (i.e. nodes connected by edges) seem generally to be pretty bad, perhaps because graphing is such an interestingly mathematical subject. It’s easy to get lost in coming up with the perfect data structure, picking the right automatic-layout algorithm (will it be based on a sort of atomic-repulsion thing? Will the edges have springs? Okay, but how much should they spring?), and so on, and thus so easy to forget about things like compatibility and documentation. One library I encountered seemed basically an excuse to show off a particularly interesting layout algorithm.

Anyway, after trying a few, I settled on vis.js. The example worked, which is always a good sign, it doesn’t have any obvious bugs (such as constant 100% CPU usage), and, most importantly, if you make your nodes smiley faces you can end up with some interesting sea-creature-looking things.
Thu
Aug 28

Short Chromium rant

In 2011, Google announced plans to remove H.264 from Chrome. They were terrible plans with no practical upsides for anybody apart from Google. H.264 is used everywhere, and three years later Chrome still supports it.

What is gone, though, is Chromium’s support for H.264. If you use Chromium you have to compile in support for the Web’s most popular video codec. So I did that — it’s not at all hard, just time-consuming. If you’d like to save yourself the pain, and use a Mac, I’ll send you a copy.

Wed
Jun 4

My favourite joke from the Swift documentation so far

let constantString = "Highlander"
constantString += " and another Highlander"
// this reports a compile-time error - a constant string cannot be modified

Mon
May 12

Evening hack: multiheaded Nokia 3310 displays

I know, I know, 84x48 pixels should be enough for anybody. But I just find that I’m so much more productive with the extra space.

SPI Flash programmer for Arduino

I’ve released an SPI Flash programmer sketch, along with a Python driver library (written for Python 3). It requires pySerial. Full docs are available on Github:

https://github.com/nfd/spi-flash-programmer

This isn’t particularly nice code, but I couldn’t find a usable SPI programmer sketch when I wanted one. This one has been tested on a couple of chips and should work, but let me know either way if you use it. 

One thing it doesn’t support is the extended command set for large SPI Flash chips, so for now it only works with chips up to 16MB.