Sat
Mar 14

ci20: Enabling the timer

This is part 4 of an ongoing series (part 1part 2part 3) about writing bare-metal code for the CI20 Creator. The last post talked about the RAM available to the CI20: 1 gigabyte of DDR, which requires initialisation, and 16 kilobytes of SRAM, which can be used by a bootloader to initialise the DDR.

Before we get to DDR, we need to give the bootloader some way to keep track of time intervals. The proximate requirement is the DDR controller requires a 200 microsecond delay, and without a timer of some sort we have no way of knowing how long that is — but timers are generally useful things to have around in any case. The JZ4780 CPU has lots of timers available to it, so the simplest thing to do is just to enable one of them.

You can check out the repository for this code:

$ git clone https://github.com/nfd/ci20-os
$ cd ci20-os
$ git checkout tags/ostimer

There are a few changes from last time, but the only really functionally relevant one is that there are two new files, timer.c and timer.h, which set up the “OS Timer” in the JZ4780 SOC. Initialisation of this timer is very straightforward, as can be seen in os_timer_init():
  • Set the frequency of count, which is some fraction of one of the available clocks — we use the 48MHz EXTCLK which doesn’t require any additional set-up, and then divide it by 16 to give us a timer which increments 3 million times a second.
  • Clear the counter value (not required, just neat);
  • Set the clock source (EXTCLK); and
  • Start the timer running.
Compile and run the code as before:

$ make bootloader.bin
$ python3 usbloader.py bootloader.bin

And you should see a message printed to the serial port once a second.

This timer isn’t using any interrupts, because we haven’t yet written any way to handle them. This means that the delay function, usleep(), simply polls the timer register in a tight loop. This is of course very bad practise generally, but it’s just about okay for run-at-boot initialisation code and short time intervals.

Sat
Mar 7

Running code on the CI20 without uboot

This is part of a series of posts about bare-metal programming for the CI20 Creator development board, though you don’t need to have read the other parts (part 1part 2) to follow this one.

Previously we were using uboot to load the code. This is pretty convenient, but it does mean that there are some mysteries about setting up the board’s hardware which uboot hides from view. What does it do? This post and the next one will explore that a little, by writing some code which doesn’t use it at all.

Where is the RAM?

One thing I took for granted previously is that the board has RAM available for us at 0x80000000, i.e. at the start of KSEG0. We know that the board has 1GB of RAM, and that RAM has to be accessible to us somewhere.

Or does it? The JZ4780 programmer’s manual is full of references to a “DDR [RAM] Controller”, which has quite a complicated set-up. But if the DDR controller has to be initialised, where does that initialisation code run?

It turns out that on the JZ4780, there is in fact a separate tiny (16k) bit of static ram, known as the tighly-coupled shared memory area, or TCSM, which solves this problem: you can copy your DDR initialisation code into TCSM and then copy the rest into DDR.

Of course, now we are faced with the problem of getting our code into the TCSM. To deal with this problem, the JZ4780 includes a small ROM capable of loading things into TCSM. This boot room is quite sophisticated, supporting NAND, SD, USB, and SPI loading.

Booting from USB

If we’re going to play around, then USB loading is quite an attractive option because it’s nice and fast (compared with unplugging SD cards and so on). Unfortunately the USB loader is a bit finicky: the device doesn’t even enumerate properly on Mac OS X (though it’s fine on Linux), and it requires a custom loader. I wrote the custom loader, so if you have access to a Linux box (or, perhaps, if you’re running Linux in a VM) you can follow along.

By default, the CI20 requires you to hold down a button to do a USB boot. That sounded a bit tedious, so I soldered some headers to the button terminals:


Connecting a switch to the headers means I can have always-on USB booting.

We’re going to use the USB OTG port as a peripheral. Remove the jumper from the pins next to the port. This turns the port into a USB peripheral, rather than a host. You can then connect a mini-USB cable from your computer to the OTG port and power up the board. If all is well, you should see a new USB device appear (run lsusb on Linux, or use USB Prober, or System Information, on a Mac). 

Serial-port communication

Rather than flashing a LED, this example now communicates over a serial port — the same one, UART4, used by uboot and the Linux kernel, so if you set up a usb-serial cable for previous parts this will work without any changes.

Running the example

I’ve created a new repository for this stuff, which you can check out using git:

$ git clone https://github.com/nfd/ci20-os
$ cd ci20-os
$ git checkout tags/usbloader

You’ll need to build the bootloader (make sure you’ve set up your environment as described in my previous CI20 posts):

$ make bootloader.bin

Now reset the board and load the example using my USB loader. Note that you will need to install pyusb before running this (and that, in turn, will require libusb0).

$ python3 usbloader.py bootloader.bin

If you get “permission denied” errors, you can set up udev rules to give yourself access to the port. Or you can use sudo if you’re naughty.

You should then get a cheery message on the serial port.

Next steps

This example hasn’t actually initialised the DDR. It’s just running straight out of TCSM at the special TCSM location 0xf4000000. Overall, the board is only just barely initialised enough to get stuff out of the serial port. Next time we’ll look at setting up the DDR so that we can begin to think about loading things back into the familiar 0x80000000 “proper” RAM, and see what other board initialisation is needed by the jz4780.

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