terça-feira, 20 de março de 2012

uClinux - Part I


A few months ago I was dumping out some old things to the trash when accidentally stumbled into those old yellowish breadboards still holding tight the magnificent 68000 cpu. I stood there for about 5 minutes admiring all the patience I had a decade ago wiring up all those pins. Suddenly a nostalgic feeling had taken over my mind, and all I could think at that moment was "I really need 5 volts".

I was about to know if the mighty 68breadboard000 was still rock n' rolling. I've connected the serial port and applied power and there it was, the system was up and running ready to accept commands from the serial port!

After playing some time with it, doing some memory dumps and tests, the same old question came back: "What the hell am I going to do with this?".

For a few days the whole system was left untouched in my desk... as usual. And one day I was googling around when the word "uClinux" made a spark in my brain. I looked to the 68000, looked back to the word "uClinux" and its fate was obvious: "Run some linux code in that old junk beauty!".

I've had an experience with uClinux in the past with a Coldfire microcontroller although I hadn't done much back in that time since I used a pre-compiled kernel image.

Dressing up for the party

The system was far from being able to run such operating system. It had a small amount of ram, the serial port and was slow and I had no timer for periodic interrupt. The flash memory was enough for the boot, but userland binaries had to be cut to the minimum.

SRAM: The system only had 128k SRAM (4x32Kb) and I had four old 128Kb around. What I didn't have was room to place those huge chips so I've wired them up in two extra breadboards (I had to dismantle a Z80 to get those!). Once wired up it was time to connect that half megabyte of ram to the 68k totaling 640KBytes of ram.
Memory upgrade: Check!

Serial port: The system had a USART chip (from the 8051 microcontroler series) wired up. It only had a 1byte buffer and without hardware flow control, buffer overruns occurred frequently. Since a uClinux kernel + romfs can be significantly large, I needed to have a way to upload the image to the system, fast!
I had this spare USB module where the interface is an 8-bit parallel bus, with 4 control signals (/rd, /wr, txe, rxf). Wired it up to the cpu upper half of the databus and connected the control signals with basic logic ports. When connected to the PC USB port it is seen as a standard (fast) serial port.
Fast serial port: Check!

Bootloader: The way I always used the system was loading to the ram and running code from there. Since I needed the ram available fot the kernel, I had to write the image directly in the flash so I've coded a simple bootloader in that beautiful assembly language (no kidding, it is really beautiful!). The working principle is simple:
  1. Wait for a key press on the serial port for 3 seconds (not accurate seconds).
  2. If no keypress, boot from a specific flash address where hopefully kernel code is located.
  3. If a key is pressed, start the bootloading part.
  4. Bootloader receives srecord files. If it detects that the destination address is flash, load everything to the ram and at the end of the load cycle, write everything to the flash (and verify). At last boot from flash.
  5. If the destination of the srecord if ram, load to ram and boot from there.

I was facing some weird behaviour during the flash write, later I realized that the bootloader can't be executed from the flash and write on it at the same time! The solution was simple: When the bootloader starts, it copies itself to the ram and then runs from there.
Bootloader: Check!

Periodic timer interrupt: This final piece of hardware was missing from the system. It is a vital component to enable any multitasking operating system to run. It is responsible to make the cpu enter a 'supervisor' mode and switch between tasks. My first approach was to use a counter and connect one of the upper bits to the interrupt logic with some latch so that I could reset it in the interrupt routine. It was a bad idea... Didn't work well so I just ended up with a simpler solution: a 555 timer! The interrupt interval is set by a resistor and capacitor. It's not accurate but it's good enough to do the job!
Timer interrupt: Check!

Let the compiling begin!

As a first approach I've used as base code the board from Weiss Electronics: SM2010. Apparently that board also uses an 68000 cpu. From the few documentation I could find I know It's some kind of industrial controller board with 6 serial ports and (at least) 2 hardware timers. The guy that ported uClinux to that board did most of the work. He ported kernel 2.0.x and for a start it was good enough. I ended up by adapting the hardware part of the kernel: hardware timer, ftdi245 serial driver and ram/rom locations for the linker.

A few compilations later there it was! The old 68000 up and running uClinux!
I've also made a small video showing the kernel bootup and some commands being issued.

But the job wasn't completed! My ultimate goal is to run kernel 3.x in this old piece of hardware!

So my next step was to make a fully functional kernel 2.4.x. and there was no SM2010 board in the kernel tree!