Adding I2C to the AVR ARM Emulator

The finished uARM board with RTC on far left, display next and ATMEGA1284p. 16MB RAM SIMM on top, SD card socket on lower right and power supply on lower left.

The finished uARM board with RTC on far left, GPIO/display next and ATMEGA1284p in middle. 16MB RAM SIMM on top, SD card socket on lower right and power supply on lower left.

So way back about a year and a half ago, I stumbled upon a really cool project where Dmitry wrote an ARM emulator for the AVR line of micro controllers.  His intentions were to prove everybody wrong that AVRs (or 8-bit micro controllers in general) can run a standard linux kernel.  His round-about way of doing this was to code an ARM emulator complete with MMU and 32-bit support from the PXA-255 series of processors.  It worked and successfully boots Linux but at a blazing 10KHz effective (emulated) clock speed.  I’ve always been fascinated with computers but more specifically older “discrete” systems because they’re just simple enough that the average tinkerer can experiment with.  So I made it my mission to not only build Dmitry’s simple 8-bit computer, but to expand upon it.  Even before I finished initially wiring up the circuit I had already concluded that I would implement I2C on it since the standard only requires 2 pins and there are many many slave devices out there to work with.

It took me 7 months just to replicate his results and boot linux with his supplied image. Granted I got frustrated in that time period and had to shelve the project for several months like most of us.  Most (rather ALL) of my trouble involved reading from the SD card.  No matter what I did, what card I used, I could not get the bootloader to read the card.  Eventually I successfully got the emulator up and running using the famous ELM CHAN SD functions.  They are not nearly as efficient as the original (which was written in assembly) but they do work and I was able to boot the image and was greeted by the prompt.

Shortly after I had it booting, I began digging through the source files to see how the emulator works and whether or not it was possible to implement I2C on someone else’s design.  Luckily he wrote the emulator in such a way that it would be easy to “plug in” another module, in this case I2C.  Over the course of a weekend, I study the Intel PXA-255 processor handbook covering the I2C module and managed to write the core functions for I2C that the emulated SoC would access. On top of the new I2C routines, I had to add to the SoC.c file the I2C initialization line

if(!pxa255i2cInit(&soc->i2c, &soc->mem, &soc->ic)) ERR_("Cannot init PXA255's I2C");

 

The lovely point-to-point wiring that makes up the hardware. I tried to keep red as +5v, green as ground, yellow as clock as address, and blue as data

The lovely point-to-point wiring that makes up the hardware. I tried to keep red as +5v, green as ground, yellow as clock as address, and blue as data

Since there was only one pin left of the AVR after wiring, I needed to figure out a creative way to get an additional one.  I decided to charlieplex the SD R/W LEDs and had my second pin. Then came time to get linux to recognize the new I2C.  This proved to be the most difficult part of the endeavor.  My original idea was to just compile the modules needed for I2C and manually insert them into the kernel once booted.  Unfortunately, this didn’t work since the modules weren’t compiled with the kernel.  I was trying to avoid it, but I had to rebuild the boot image.  At this point I was definitely out of my comfort zone as I had never attempted to build a boot image before.  Luckily Dmitry included a script to build the actual boot image, as well as the final bootloader, RAM disk files, and kernel configuration for the ARM device he is emulating, which happens to be a Palm TE2.  It took me another couple of days to figure out how to successfully build a RAM disk (again, something I had never tried before).  In the end, I had compiled the kernel with the needed I2C support, built the modules, built the pvDisk module (how the emulator accesses the SD card), built the RAM disk, and bundled it all up with the bootloader into a boot image.  The next task was to copy this image to the SD card’s boot partition and give it a try.

At this point I began using Dmitry’s PC build of the emulator so I could test everything out on my computer way faster than I could on the actual AVR hardware.  Unfortunately the kernel still didn’t find the I2C “hardware” and I was right back to where I started.  Dmitry pointed out that I should add simple debugging messages to the various parts of the code (and kernel) so I could see when the I2C unit gets initialized.  I did that, recompiled/built everything, and still no debugging messages.  I began questioning whether or not the Palm TE2 actually had I2C devices but he assured me that it did.  I decided to add debugging messages to the UART portion of the kernel since I know for a fact that those are being initialized correctly.  Sure enough, I get the UART debug messages during boot up but still no I2C.  I begin diving further into the kernel source code to see how the individual parts (UART, I2C, etc) get initialized and thats when I realized that the I2C initialization was completely missing from the Palm TE2.  Most other ARM devices do have the needed code, but the ‘TE2 was completely missing it.

In the palmte2.c file under static void __init palmte2_init(void) I added

pxa_set_i2c_info(NULL);

Once the line was added, I was greeted with a screen full of I2C debug messages. Unfortunately, once again, I had MORE problems.

THE SETBACK

I was doing all of my development under Ubuntu in a virtual environment on my Mac. For some reason the hard drive died and I lost everything. If I were smart, I would have made backups but I didn’t. Frustrated, I shelved the project for nearly a year.

CLEARING THE DUST

The actual ARM emulator board had been sitting on the corner of my desk for a year collecting dust and mocking me the entire time.  I finally decided to take up the endeavor once again and tried adding I2C to the emulator.  Luckily while I was conversing with the original author of the project, I had emailed him the I2C module I wrote to get his opinion on it.  I was able to retrieve this older copy and mesh it again with the  original code.  During the process I fixed several bugs that I found (i.e. missing continued transmit code).  I was once again able to recreate my original success.

Now that I had a different perspective of the task (thanks to a years break) I was really able to make progress. The first task was to download and compile the i2c-tools package but first I had to activate the swap on the emulated machine.  To do this I edited /etc/fstab and added

/dev/pvd3 none swap sw 0 0

then activated the swap with

swapon --all

Now that I had some extra memory to work with, I had to add the path of gcc

PATH=$PATH:/usr/bin

then make the actual i2c-tools

make

after a few minutes I had the i2c-tools compiled.  I discovered that I needed a way to monitor the emulated I2C hardware and I added logging to the I2C module.  This way I have low level and register level logging.  In low level logging, I can see what individual bits are sent (or expected to be received) as well as start, stop and continued start conditions and acknowledge bits.  In register level logging, I see which of the 5 PXA-255 I2C registers are accessed (read or write) and what the before and after results are. The i2c-tools package includes i2cdetect which allows you to scan the I2C bus for slave devices.  I used this quite extensively to test and debug the code. I had a lot of trouble determining how the status bits actually change in the ISR (I2C Status Register).  Unfortunately the processor manual provided by Intel doesn’t provide too much detail in that respect.  For example, it was not clear on how the ACKNAK bit is cleared in the ISR register when a slave address isn’t acknowledged. Most of my work was done by trying to detect an address, have the I2C module throw an error, then review the I2C logs for register changes and see what was transmitted (if anything). In the case of the ACKNAK bit, if I didn’t clear it after a register read, then the i2c-tools software would eventually fail (it wasn’t cleared in software). After a while, I had enough of the I2C emulation covered that I needed to test this on real chips.  Unfortunately it would be too difficult to interface an emulated ARM system on my laptop to a real world I2C device so I made some virtual devices.

The window at the lower right is the emulator running on my computer. The window on the lower left is the fake contents of the I2C chip. The uARM in the left window just dumped the contents contents of the I2C chip using the i2cdump tool which was part of i2c-tools

The window at the lower right is the emulator running on my computer. The window on the lower left is the fake contents of the I2C chip. The uARM in the left window just dumped the contents contents of the I2C chip using the i2cdump tool which was part of i2c-tools

I wrote a sort-of chip emulator, where when a slave address is “transmitted” the current directory is scanned for that chip address, if it exists, then it responds with an acknowledge, otherwise a non-acknowledge. If the chip does exist then its contents can be read or written to.  Although this chip emulation works, it’s not 100% complete and has several pitfalls. I was able to use my new virtual chips to test more of the I2C module with the help of i2cget and i2cset, again, part of the i2c-tools package.  After I had everything fixed in terms of the I2C module, it was then time to focus on the real world slave devices.

I had already determined that I would interface a RTC and GPIO chip to the ARM emulator.  I decided on the DS1307 and PCF8574, respectively. Why? Because the linux kernel has support for both of these chips.  Again, I had to recompile the kernel to support them and build the respective modules.Why modules? Because its easier to unload and reload a module if something hangs than to reboot the entire emulator.  To build an entire working image, this is what I did: I started with the SD image Dmitry provided and his source files. You’ll also need to find yourself an ARM compiler and the kernel source files.  I chose to use  2.6.34.13. I tried 3.6 but the kernel would fail to boot.  Be sure to set up cross-compiling in the kernel Makefile with your ARM compiler of choice. Copy the kernel configuration to the kernel source directory

cp kernel.config linux-2.6.34.13/.config

then configure the kernel and add I2C support, i2c-dev support, and the ds1307 and pcf857x support

make menuconfig

then make the kernel

make

and compile the pvDisk module. Make sure to edit the Makefile for the kernel source directory and compiler of choice

pvDisk/make all

now copy the resulting pvDisk.ko module into the RAM Disk folder, overwriting the old

cp /pvDisk/pvDisk.ko ./rd.img-Files/

and make the RAM disk image

find ./rd.img-Files/ | cpio -H newc -o > initramfs.cpio
cat initramfs.cpio | gzip > initramfs.img

then make the zImage

linux-2.6.34.13/make zImage

now make the boot image

./mkbootimg.sh BOOTLOADERS/ELLE.bin linux-2.6.34.13/arch/arm/boot/zImage initramfs.img > bootimage.img

mount the linux partition of the SD image and install the modules

sudo mount -o loop,offset=16384 jaunty.img /mnt
sudo linux-2.6.34.13/make modules_install INSTALL_MOD_PATH=/mnt
sudo umount /mnt

and finally write the boot image to the SD image

dd conv=notrunc if=bootimage.img of=jaunty.img bs=512 seek=786464

and the SD image is complete. Now its time to boot the emulator on the PC

./uARM jaunty.img

With the help of the chip emulation I was able to verify that the ds1307 module loads correctly and works.  I had to create the file to emulate the ds1307 chip called 1101000.i2c (the chip address is 0×68 which happens to be 1101000 in binary).  I then filled it with a dummy time according to the datasheet for the chip.  To enable the DS1307 in linux I did the following

modprobe i2c-pxa
mount -t sysfs sysfs /sys
echo ds1307 0x68 > /sys/class/i2c-adapter/i2c-0/new_device
modprobe rtc-ds1307
mknod /dev/rtc1 c 254 1

now the new rtc node is created at dev/rtc1 and can be used by the software.  Unfortunately, because the chip is emulated and isn’t actually ticking, I couldn’t use the hwclock command.  You can still see the time by inspecting /sys/class/rtc/rtc1. On the actual hardware with the actual DS1307 chip, however, the hwclock command will work:

hwclock --show --rtc=/dev/rtc1

As for the PCF8574, I wasn’t as lucky.  Apparently, the driver written for the kernel I was using has several problems and fails to load.  The suggested fix was to update your kernel.  That too isn’t a solution so I tried copying the source from the 3.6.1 kernel (GPIO-pcf857x.c) and placing it in my 2.6.34.13 kernel (pcf857x.c) as suggested on a forum. I tried recompiling the kernel and surprisingly there weren’t any broken dependancies or missing headers. After remaking the SD image I found that the GPIO expander chip now works and I’m able to load the driver

modprobe p2c-pxa
mount -t sysfs sysfs /sys
echo pcf8574 0x38 > /sys/class/i2c-adapter/i2c-0/new_device
modprobe pcf857x

now that the GPIO expander is found, its time to use it

echo 248 > /sys/class/gpio/export
echo "out" > /sys/class/gpio/gpio248/direction
echo 1 > /sys/class/gpio/gpio248/value
echo 0 > /sys/class/gpio/gpio248/value

This brings out one of the pins as GPIO pin 248 where I can now set the direction as output then write a 1 or a 0 to it.  Although this works and proves that the I2C module works, I still wanted to come up with a way to show off everything.  What better than a clock?  I was a little hesitant that it wouldn’t work since the emulator is running at around 10KHz.  Still, I tried.

 

Simple display board I etched to hold the PCF8574 GPIO expander and HDLS-2416 display

Simple display board I etched to hold the PCF8574 GPIO expander and HDLS-2416 display

I wrote a simple program to open two handles to /dev/i2c-0. One for the GPIO at address 0×38 and a second for the RTC at 0×68.  It simply reads the RTC and outputs to the GPIO expander which is wired to a LED dot matrix display I happened to have (HDLS-2416). I still had one I/O available on the GPIO which I wired to a tactile button.  This allows me to set the time from within the program.  What started as a simple program quickly grew into something a lot more complex than I first expected.  Thanks to the way the digits are stored in the RTC as BCD, there was a lot of conditional statements and masking/shifting to recover the digits.

Display board with the LED matrix display covering the GPIO expander

Display board with the LED matrix display covering the GPIO expander

I was worried that the program would be too slow to be useful, however it has been operating for over a week now and I have yet to find any problems.  I’m also able to set the time thanks to the included button.  To finish off the design, I etched a small board to hold the PCF8574 and HDLS-2416 display.

PROBLEMS ALONG THE WAY

I had been using my Saleae logic probe to monitor the I2C communication and ensure bytes were being transferred correctly.  This helped immensely as it allowed me to spot incorrect timings for my stop condition as well as confirming what data had been transferred. Unfortunately, I discovered that when the Saleae was disconnected from the computer, data was no longer transferred correctly and I would constantly get errors.  It took me several days to discover that I had to disconnect the logic analyzer from the circuit when not connected to the computer.

Another problem I found was that I ordered a lot of 5 PCF8574 chips online.  Trusting that I would get just that, I never thought to actually check all the markings on the chip.  I saw the PCF8574 and was satisfied but the actual chip was the PCF8574A.  Those two chips are nearly identical except for their address.  The address for the A version starts at 0×38 (with 3 pins on the chip controlling the lower 3 bits of the address) and the regular version started at 0×20.  It wasn’t until I re-read the datasheet that I thought to check the chip a little closer and thats when I discovered the difference.  After I corrected the address, the chip began responding.

The RTC board that I ordered on eBay a long time ago had finally found a use in this project.  It seems that this board was designed by a complete amateur that simply didn’t care about how well it functioned.  It appears to be a common design as its found all over the place. To start with, they added a charging circuit to the backup battery.  Anyone should know that you can not charge a coin cell battery. Second, they added a voltage divider between the coin cell and the RTC to lower the voltage that the RTC sees from the charging circuit while powered.  The voltage divider adds an impedance to the battery which may cause problems. All of this was discovered while I was trying to remove the pull-up resistors that they included on the board.  I removed the diode that made up the charging circuit and disabled the voltage divider by removing the resistors and shorting one of the pads. Once I did this, RTC read errors I was randomly getting disappeared completely.

RTC and GPIO attached to the raspberry pi to test the clock software

RTC and GPIO attached to the raspberry pi to test the clock software

I was trying to find a simple way to reduce the turnover time of correcting code errors and recompiling the clock program when I decided to work on my raspberry pi board.  This computer already has linux and I2C so it was only a matter of transferring my program over and connecting the RTC and GPIO boards to the pi. I was able to get the bugs worked out in just one night this way.  Since the pi works off of 3.3v and the RTC works on 5v, I had to remove the built in pull-ups on the RTC, which was when I discovered the above problems.  The funny thing was that I was chasing phantom bugs in the I2C module relating to the RTC dropping out until I connected it to my pi and had the exact same problem.

As I had already mentioned, I had problems with the PCF857x driver included in the 2.6.34.13 kernel.  When loading the module and notifying the I2C system of the device I should have received pcf857x 0-0020: gpios 0..7 on a pcf8574 but instead I was getting probe of 0-0020 failed with error -22 It seems that this problem is related to a missing base assignment in the driver and it was suggested to pull the source from a 3.1 kernel or newer which has the fixed code.  Luckily this fixed the problem, however, in the end I didn’t use the the GPIO in this manner.

Since I can’t use the SD card reader on my Mac under Ubuntu, I was transferring the image to the Mac side of things then trying to use the dd command on the Mac to write the SD card.  Unfortunately I kept getting really strange errors when attempting to boot with the card such as compressed data violation.  All of my SD cards did this so I was really confused.  Ultimately I determined the problem was with how the image was transferred from Ubuntu (under Parallels) to Mac.  I bought a cheap SD to USB dongle and it worked after that.  Funny thing was that trying to dd a file with an img extension to the SD card on a Mac does some really weird things.

CLOCK SOFTWARE

My first attempt at displaying the time.  I was close but unfortunately BCD math is not fun to code

My first attempt at displaying the time. I was close but unfortunately BCD math is not fun to code

The clock software was my first real attempt at trying to write a c program under linux so as such I’m sure its full of bad practices. With that said, the program works and is definitely useable.  When starting the program you can append it with a -h for a short description of how it works or a -c for continuous mode.  Otherwise, simply running the program would get the time from the RTC and show it on the display attached to the GPIO then exit. The program must convert from packed BCD that the RTC returns to individual digits that the GPIO can output.

Another view of the display showing numbers :)

Another view of the display showing numbers :)

When displaying a digit, the program writes the address of the digit (2 bits), a 0 for active high write (1 bit), place holder for input (1 bit), and the digit itself (4 bits) to the GPIO as an 8-bit value. It then toggles the bit representing write to the display and writes the byte out again to the GPIO.This displays one digit to the display at the position determined by the 2-bit address (00 for right most and 11 for left most).  Since the display is capable of representing 7-bit ASCII characters, the upper 3 bits are hardwired to only allow the display of 0-9 and 6 additional characters, determined by the lower four bits of the GPIO. The input pin must alway be written to as a 1 since the GPIO is a “quasi-bidirectional” device.  What this means is that there is just one register. Write a 1 to a given bit and the corresponding pin goes hight, a 0 and it goes low.  For input, you write a 1 to the desired bit then read from the GPIO, paying attention just to the bit(s) you wrote a 1 to.

The final uARM board showing the current time

The final uARM board showing the current time

They will read as a 1 if the input is high or 0 if the output is low. The software reads the GPIO and checks if the bit is a 0 meaning the button is pressed down.  If the button is down then it enters the setting mode where the first digit alternates between the current number and a question mark. Pressing the button advances the current digit. after 5 seconds then next digit is selected and so on until all four digits are entered.  The program then corrects the digits and converts them to packed BCD to write to the RTC.  Overall, it take the program 2.9 seconds to read the RTC and display all four digits.  There are no delays and the program is running at full speed on the emulated hardware.

ZORK ON uARM

Zork running on uARM.  It's actually running on the PC version but I also successfully got it running on the AVR hardware too and surprisingly its still quite usable.

Zork running on uARM. It’s actually running on the PC version but I also successfully got it running on the AVR hardware too and surprisingly its still quite usable.

Just for fun I decided to download the source for frotz and try compiling it to play the classic Zork game. Since this image doesn’t include ncurses, which frotz needs to compile, I decided to compile dumb frotz.  This just builds it without the ncurses library so some games wont display correctly but it still plays Zork just fine.

Downloads

Unfortunately I don’t have the bandwidth to host all of the complete files such as the SD card image, but if you’ve already attempted Dmitry’s build then you already have everything you’ll need.  I’ve zipped up the I2C files that need to be included with his files, along with PC and AVR executables that are ready to run. Just remember to make the changes I’ve made above as well as opening up the pins needed for sda and sck.

uARMi2c

CREDITS

I’d like to thank Dmitry for all of his work on the original ARM emulator. His page can be found here and his uARM project can be found here.

10 Responses to Adding I2C to the AVR ARM Emulator

  1. Pingback: Making The Worst Linux PC Useful

  2. Pingback: rndm(mod) » Making The Worst Linux PC Useful

  3. Pingback: Making The Worst Linux PC Useful - RaspberryPiBoards

  4. Pingback: Belgaum news | About Belgaum | Belgaum information | Belgaum district | Belgaum city | Belgaum Hotels | Belgaum People | Belgaum tourism | Belgaum entertainment | Belgaum students | Inside facebook | Hack | make use of | technical news | | Making The Wors

  5. Pingback: Making The Worst Linux PC Useful | Hack The Planet

  6. Pingback: Making The Worst Linux PC Useful | Maker of Meta

  7. kbd says:

    Good stuff. I found you can also get the compiler to work by increasing the RAM size SoC.c from 16mb to 32mb (change one define).

    • Kyle says:

      Very true. If only it were that easy to change the memory size on the actual hardware. At least with an active swap, I could compile the tools in a few weeks on the AVR.

  8. Pingback: Molten Madness | www.wengenroth.co

  9. jornada660 says:

    Great work!!!
    Because you have tested the Dmitry’s project, can you tell me if you had to modify a lot of stuff?
    Could you upload your files somewhere? It would be great if you share those files, still if these are very similar to Dmitry’s ones.

    Thanks a lot and congratulations for the great work!

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>