Linux

lpicp: Turn any board running Linux into a PIC programmer

This is probably not what you think it is. It is not an application that controls ICD2, PicKit2 or any form of custom LPT PIC programmer for a PC (you can use MPLAB X for that). What lpicp does is provide a user space application and kernel driver to perform the functionality of a PIC programmer on embedded systems running Linux. After deploying these modules onto such a system (and assuming the hardware is layed out properly), you can run something like:

lpicp -x write -d /dev/icsp0 -f my-pic-program.hex

and have your embedded system program a Microchip PIC microcontroller via an ICSP bus.

Implementing the ICSP protocol and bus

Microchip has devised a simple 4 wire bus to allow for programming the PIC devices. In reality, the majority of the physical protocol is over 2 wires – pgc (clock) and pgd (data). The bus master (the programmer) clocks out 4 command bits followed by 16 data bits. The commands are specified in the flash programming spec for each device but they can be divided into three main groups:

  • Read
  • Write
  • Execute instruction

To implement a high level operation like reading a device requires a mixture of commands, spanning across all the three groups; simplisticly you’d have to execute instructions to prepare the PIC, write the address pointer and read data. You really must read the flash programming spec to understand the specifics, but the jist is the same for all high level operations (erase/read/write).

Since no hardware controller exists for operating the bus transactions, we are required to bit bang it. The hardware protocol itself is very simple (although very inconsistent across operations and devices). I had the option of implementing the bus protocol entirely in userspace and using the platform independent GPIO infrastructure but this does not achieve the desired bus timing (despite the fact that the PICs are very lax in their timing, especially in maximum values, I still wanted the bus to look as clean as possible).

To do this, I offloaded all bus transactions to a platform driver (mc_icsp) using callbacks to the platform (which toggles the GPIOs). All timing is configured when registering the platform driver and user space interacts with it via /dev using 4 ioctls:

  • transmit: send 4 bit command and write 16 bits of data
  • receive: send 4 bit command and read 16 bits of data
  • send command only: send only the 4 bit command with a configurable waveform
  • send data only: send only 16 bits of data

Userland

The user space application, lpicp, takes care of receiving the command from the user, parsing Intel HEX records and performing the higher levels ICSP protocol. This is very device specific so there is framework in place to allow for adding devices without ugly ifs() and #ifdefs.

The application is built using cmake, which outputs liblpicp.a and an lpicp executable. The executable adds a command line layer, but you can just as well link to liblpicp.a to integrate PIC programming into your application. The lpicp executable can be run with the following arguments:

  • -x, –exec: r, read | w, write | e, erase | devid
  • -d, –dev: ICSP device name (e.g. /dev/icsp0)
  • -f, –file: Path to Intel HEX file
  • -o, –offset: Read from offset, Write to offset
  • -s, –size: Size for operation, in bytes
  • -v, –verbose: Verbose operation

The application uses libGIS‘s ihex.c module to parse the Intel HEX record file outputted by PIC compilers. The files contain data records to be written to program, configuration and eeprom memory. The application knows where to burn each record according to its address.

Getting the source

This project is really in its infancy and will probably only ever serve as reference. I was contracted to implement this for PIC18F452 and PIC18F4520 and this is what works. Browse the README @ github to understand the limitations of the current version and build instructions.

You can get the lpicp code @ the lpicp github repo and the driver can be found here.

Repairing FreeNAS after a power outage (FreeNAS rebooting)

For reasons beyond me, I have a storage server set up with FreeNAS. I find it convenient to have all of my movies/music/TV shows on a PC that sees no action besides reading and writing to its drives. This weekend, while streaming a show from my storage server, the power went out. The server is connected directly to mains, so it went down – hard. To be fair, my Ubuntu based media center PC also died, never to recover. After a bit of troubleshooting and an annoying secondary problem, it seems to be up and stable.

My FreeNAS server is based on a 4 year old AMD 3200+ PC. Before setting everything up I made sure to clean the motherboard and CPU mount, applied new thermal grease on the CPU heatsink and upgraded the PSU to a Thermaltake 480W PSU – I really wanted to reduce chance of having any electrical/heat related issues that would take out the system. FreeNAS was loaded on a USB drive and a single 1.5TB drive was attached to the SATA bus. The server sat alone in a closet, connected via Gigabit ethernet to my home LAN and remained there. Headless, keyboard-less and monitor-less.

After the outage, the server did not respond to pings. I hooked it up to my monitor and attached a keyboard. It seemed like the BIOS did not even attempt to load an OS. I checked the BIOS settings and it seemed to revert to booting from HDD. Triumphantly I changed it to boot from USB and rebooted. Nothing. I then went ahead and restored BIOS defaults and again changed to boot from USB, just to start from a clean slate seeing as something freaky went on with the BIOS settings. No dice.

I tried yanking the USB key holding FreeNAS but that showed nothing. I then yanked the SATA cable from the HDD and finally something – the BIOS seemed to print something about Nvidia boot loader agent, but again hung. I knew something had shifted so I replugged the USB drive and voila – FreeNAS loaded. I let it load again with the HDD connected and it seemed I was successful.

Plot twist: FreeNAS reboots itself every 20 minutes

I patted myself on the back, called the wife, sat on the couch and loaded “The Wire” episode we were watching. After a few seconds, VLC crapped out indicating failure to read from file. The storage server was down again. I was smart and pessimistic enough not to disconnect the server from the monitor so I could get a glimpse of what was happening. After a bit of investigating, I saw that FreeNAS would reboot every ~15-20 minutes, claiming the following panic:

panic: ffs_blkfree: freeing free block

So it’s a file system issue, methinks. I then performed the following:

  1. Via the FreeNAS shell I ran “mount” to see which partition my storage mount was on (it was /dev/ad4p1)
  2. Via the FreeNAS web interface, I deleted the shares on the drive and deleted it from the drive management (this does not destroy the data on the drive, just detaches it apparently). Reboot
  3. Manually unmount mount in /mnt (in my case, “umount /mnt/storage1″ – this depends on your setup). Reboot. At this point the partition should not be in use by FreeNAS
  4. Time to fix the partition. I ran “fsck -t ufs -y /dev/ad4p1″ several times, until no errors appeared. Reboot
  5. Via the FreeNAS web interface, added the drive (making sure pre-formatted was set to UFS). Re-added the share

That’s it. FreeNAS is up, running and stable… until the next power outage that is.

U-Boot / Linux bringup by example

This post describes the steps required for setting up an Ubuntu based compilation and debugging environment for U-Boot and Linux 2.6 w/a JFFS2 filesystem on a custom PPC board, using an Abatron BDI3000. It mostly discusses the environment and doesn’t dive too much as to how to customize the packages for your specific board (this is the hard part, but also extremely board specific). While you will probably not follow this guide from start to finish, it serves as a reference for what is the minimal amount of work required to build U-Boot and Linux for a PPC8xx board and a supplement to the DENX DULG, which is a must read.

It assumes that Ubuntu is already installed on the station, but nothing else. On the host side we mostly need the ELDK – a single package that contains all the cross tools needed (compiler, linker, debugger, tools to generate a file system, etc.).

A basic U-Boot/Linux target requires four things:

  • A U-Boot binary (u-boot.bin) generated by customizing the U-Boot sources for your board, compiled using a cross-compiler (ppc_8xx-gcc in our case) and burned to the correct offset in the flash
  • Linux image (uImage) generated by compiling the untouched Linux sources
  • A Flat Device Tree BLOB (.dtb file), generated by compiling (using a specialized compiler) a textual DTS file which describes busses, offsets and devices of the target board
  • A Root file system containing all the files we’d like to appear on the target once it’s up, packed into a single file. This file is generated by packaging up a directory from the ELDK which contains all the tools compiled for the architecture using the appropriate file system maker (in our case, mkfs.jffs2).

Step 1: Getting the sources

In this step we install git (the source control system), eldk (the development environment), U-Boot (the bootloader) and Linux (the OS). Start by getting eldk 4.2 for PPC (iso) from here (The file is under /pub/eldk/4.2/ppc-linux-x86/ppc-2008-04-01.iso). Keep in mind that this may be very slow as the file is 1.9 GB; I used GetRight on windows to pull the file from all the mirrors concurrently.

Create a working directory in your home directory:

mkdir ~/dev
cd ~/dev

Get git:

sudo apt-get install git-core

Get U-Boot sources:

git clone git://git.denx.de/u-boot.git u-boot/

Get Linux sources:

git clone git://git.denx.de/linux-2.6-denx.git linux-2.6-denx/

Step 2: Installing the ELDK

Create an installation directory under your working directory (e.g. ~/dev/eldk)

mkdir eldk
cd eldk

Create a mount point for the ELDK installation CD and mount it (prepend the path of the iso to the file):

sudo mkdir /mnt/eldk-inst
sudo mount -o loop,exec ppc-2008-04-01.iso /mnt/eldk-inst/

Install the eldk for the ppc8xx architecture

/mnt/eldk-inst/install ppc_8xx

Add eldk to path by appending the following in ~/.bashrc using a text editor of choice (make sure to show hidden files). After this is done, make sure to re-open a terminal window for the next steps:
PATH=$PATH:~/dev/eldk/usr/bin:~/dev/eldk/bin
export PATH

Test this by running the following in your dev directory:

ppc_8xx-gcc

You should see the standard gcc “no input files” error.

Step 3: Preparing a JFFS2 filesystem

The ELDK contains a basic yet functional file system that we can use. It is packaged as a ramdisk image and we need to extract it so that we can build a JFFS2 image. Strip the prepared ramdisk of its 64 byte header, revealing a gzipped file. Unzip it to receive ramdisk_image

cd ~/dev/eldk/ppc_8xx/images
dd if=uRamdisk bs=64 skip=1 of=ramdisk.gz

Mount the image and give everyone access/execute permissions to it

sudo mkdir /mnt/rootfs-inst
sudo mount -o loop ramdisk_image /mnt/rootfs-inst
sudo chmod -R 777 /mnt/rootfs-inst/

Generate the JFFS2 image assuming a sector size of 128KB (0×20000), in this case

mkfs.jffs2 -b -r /mnt/rootfs-inst -e 0x20000 -o image.jffs2

Step 4: Finishing up environment installation

The development station requires a TFTP server for two reasons: the BDI3000 TFTPs a configuration file each time it loads and U-Boot will fetch the Linux images via TFTP once it is burnt to flash. We must therefore install a TFTP server and set up its directories.

First, we’ll install atftpd:

sudo apt-get install atftpd
sudo gedit /etc/default/atftpd (modify to USE_INETD=FALSE)
sudo invoke-rc.d atftpd start
sudo chmod 777 /srv/tftp

We then create three directories @ /srv/tftp:

mkdir /srv/tftpd/u-boot
mkdir /srv/tftpd/linux
mkdir /srv/tftpd/bdi3000

Configure the BDI’s server IP address. See BDI documentation for more info how to do this.

Step 5: Compiling U-Boot to receive a burnable u-boot.bin

Customize U-Boot for your board. For a custom board with SDRAM and 8MB Flash, I had to:

  • Create a directory under /board/myboard and have a Makefile, config.mk, u-boot.lds and myboard.c. You can copy these files from the standard ep8xx board
  • myboard.c implemented the following functions:
    • checkboard: Just printed a banner
    • initdram: Initialize the SDRAM
    • boardpoweroff: Reboot hook (didn’t implement anything here)
    • ftboardsetup: Fixed up the FTD BLOB passed to Linux with information gathered by U-Boot
  • Add a header file under include/configs/myboard.h with all the preprocessor directives. Again, you can reference the standard ep8xx board, but make sure to define CONFIGOFLIBFDT and CONFIGOFBOARDSETUP. Otherwise Linux will not boot because it doesn’t receive the FDT BLOB
  • Add the board to the main Makefile (you should be able to easily understand using ep8xx as an example) Clean, just to make sure

    make distclean make clean

And then compile it

make my_board
make

Step 6: Compiling Linux

Start by installing ncurses, required for menuconfig

sudo apt-get libncurses5-dev

At this point you will have to customize Linux. For PPC this mostly means creating a compilable device tree file (with a .dts extension) which describes the offsets of the core peripherals and such (instead of instantiating drivers in code). This is not trivial and is yet another syntax/structure to learn, but at least you have examples in the form of dts files for existing boards. In addition, you will also have to create a default configuration in arch/powerpc/configs, a custom platform in arch/powerpc/platforms (which will, at least, initialize I/O pins) and modify arch/powerpc/platforms/Kconfig + arch/powerpc/platforms/Makefile to support your new platform. Simply go by example of existing boards and check out this PDF.

Build Linux and the FDT BLOB (DTB file)

make ARCH=powerpc CROSS_COMPILE=ppc_8xx- my_board_defconfig
make ARCH=powerpc CROSS_COMPILE=ppc_8xx- uImage
make ARCH=powerpc CROSS_COMPILE=ppc_8xx- my_board.dtb

Step 7: Burning and configuring U-Boot

The following sections assume the following offsets: Flash starts at 0xFF800000 which is where the Linux kernel resides (until 0xFF9DFFFF), the DTB file resides at 0xFF9E0000 until 0xFF9FFFFF, the JFFS2 filesystem image resides at 0xFFA00000. Finally, U-Boot resides at 0xFFF00000.

Set up your BDI with a configuration script that will allow you to attach to the board and erase its flash. Define the sectors on which u-boot will reside so that the erase command will know what to delete. Copy u-boot.bin (from ~/dev/u-boot) to /srv/tftp/u-boot and then telnet to the bdi.

reset
erase
prog 0xfff00000

Open a serial connection to the target. Setup u-boot to auto-load the kernel at 0xFF800000 and pass the DTB @ 0xFF9E0000.

setenv ethaddr 00:e0:6f:00:00:01
setenv ipaddr [target-ip-address]
setenv serverip [your-pc-ip-address]
setenv bootargs root=/dev/mtdblock2 rw rootfstype=jffs2
setenv bootcmd bootm 0xff800000 - 0xff9e0000
setenv bootdelay 3
saveenv

Step 8: Burning Linux, the DTB and the Root filesystem

Copy image.jffs2 created earlier and uImage, my_board.dtb from arch/powerpc/boot to the /srv/tftp/linux directory. Using the U-Boot console, download the images to RAM via tftp. After each download, note the size printed by u-boot in hex: Bytes transferred = 1284822 (139ad6 hex). You will need this for the next step.

tftp 0x100000 /linux/image.jffs2
tftpboot 0x400000 /linux/uImage
tftpboot 0x800000 /linux/my_board.dtb

Burn the images to the flash

erase 0xffa00000 0xffcfffff
erase 0xff800000 0xff9fffff
cp.b 0x400000 0xff800000 [linux-size-in-hex, e.g. 139ad6]
cp.b 0x800000 0xff9e0000 [dtb-size-in-hex]
cp.b 0x100000 0xffa00000 [jffs2-size-in-hex]

Power cycle the board – Linux should be up. Easy!