Posts Tagged avr

Running a Z-machine on an AVR

This blog has moved and won’t be updated here anymore. It’s now at blog.dsp.id.au

.

zorkCan you make a Z-machine console – a virtual machine for text adventure games –  complete with screen and keyboard on an AVR?

No. Well, probably not on a ATmega328P.

I got the code for ZIP, and stripped out all of the code for reading files and displaying stuff on the screen.  This let me compile it with avr-gcc.

I looked at the result of objdump:

$ avr-objdump -h zip_linux

zip_linux:     file format elf32-avr

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .data         000001e4  00800100  0000579c  00005830  2**0
                  CONTENTS, ALLOC, LOAD, DATA
  1 .text         0000579c  00000000  00000000  00000094  2**1
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  2 .bss          00000ac8  008002e4  008002e4  00005a14  2**0
                  ALLOC
  3 .stab         0000dcec  00000000  00000000  00005a14  2**2
                  CONTENTS, READONLY, DEBUGGING
  4 .stabstr      00003aad  00000000  00000000  00013700  2**0
                  CONTENTS, READONLY, DEBUGGING
  5 .comment      00000022  00000000  00000000  000171ad  2**0
                  CONTENTS, READONLY

The good news is that the code (the .text section) fits into 22k.  That leaves 10k for the screen rendering code (probably from TellyMate), the keyboard and the SD card code.

The .data section is quite small too, which means there aren’t many strings in the interpreter itself.

The problem is the .bss section, which gets copied to RAM on startup.  It’s 2760 bytes, much more than the 2048 which this chip has.

Let’s look at this further:

$ avr-objdump -t zip_linux | grep bss | sort -t $'\t' -k 2
...

0080030c g     O .bss    00000004 pc
00800da6 g     O .bss    00000006 __iob
00000114 g       .text    00000010 __do_clear_bss
00800552 g     O .bss    0000004e lookup_table
00800436 l     O .bss    00000100 record_name
00800332 l     O .bss    00000100 save_name
008005a0 g     O .bss    00000800 stack

There’s a 2048 byte stack, which fills the entire memory!  The Infocom games might only need a 1k stack, which will help.

Tellymate has a 38×25 screen, which needs 950 bytes.  This could be brought down to 10 lines, but games might be tricky to play.  The ZX80 didn’t store a full line if it didn’t fill the width of the screen, but adventure games tend to be wordy so this won’t help much.

An SD card library needs about 600 bytes of RAM, which blows the budget.  We’d have to go without a filesystem on the SD card, because it takes up too much flash space.  It sounds like the 512 byte buffer might be optional.

4k of RAM should be plenty.  But with ARM chips being much cheaper than the larger AVRs, that might be the way to go – if I can sort out the jittering image problems.

You might be able to squeeze the interpreter only onto the chip, and communicate with it via its serial port, or another AVR running TellyMate.

Update: It turns out I wasn’t the only one to look at this – Rossum implemented it on the 2560-byte ATmega32U4, but needed to store a 1MB swap file on the SD card!  I don’t know why you need that much – maybe you don’t – but I forgot to include dynamic memory and room for the stack.

Advertisements

Leave a Comment

A very cheap AVR dev board

I bought one of these boards for about $4 on eBay.  It’s based on the USB ASP – a cheap AVR programming board.  I remember seeing a post on Hack A Day a while ago about using them as a general purpose dev board, with 5 lines broken out to the black pin header.  By removing a link on the bottom of the board, you can get 3.3V from a regulator.

First I wanted to see what the stock firmware was.  I shorted J2, which connects the reset pin to the header pins, allowing the chip to be programmed.  I dumped the data, and saw this:

00000000  00 00 01 01 02 02 03 03  04 04 05 05 06 06 07 07  |................|
00000010  08 08 09 09 0a 0a 0b 0b  0c 0c 0d 0d 0e 0e 0f 0f  |................|
00000020  10 10 11 11 12 12 13 13  14 14 15 15 16 16 17 17  |................|
00000030  18 18 19 19 1a 1a 1b 1b  1c 1c 1d 1d 1e 1e 1f 1f  |................|

It sounds like that’s what you get when the chip is locked.  I used avrdude to erase the chip, and I was able to program it again using the original firmware from the USB ASP site.  I had a quick look at the fuse bits, and they seem to be correct.  I haven’t actually tried this firmware, so no doubt some of the pins are in different places.

The pinout appears to be the same as the original. The pins themselves are different because of the chip packaging:

D+ 13, 32 PB1, PD2 (INT0)
D- 12 PB0
MOSI 15 PB3 (MOSI)
RST 14 PB2
J2 29 RESET
SCK 17 PB5 (SCK)
MISO 16 PB4 (MISO)

The price is amazing since the AVR stick costs about $10, and contains fewer parts.  Perhaps I should retry my V-USB experiment with it.

A while ago I found some V-USB tutorials, but I haven’t gone through them yet.

Comments (1)

Run a V-USB demo on an AVR stick (Easylogger)

This article describes how to run one of the V-USB demos on an AVR stick (which is an Easylogger clone).

V-USB is a firmware-only USB implementation for AVR 8-bit microcontrollers.  This means that it’s possible for most AVR chips to communicate via USB, even though they have no hardware support for that.

The AVR stick

The AVR stick is a tiny circuit board that contains the minimum components to get a somewhat useful USB device, based on an ATmega85.  It costs about $10, and is available in Australia from Little Bird Electronics and also from Sparkfun.

I’ll use the USB HID class, because it doesn’t require any fancy drivers to get running.

First, grab the V-USB code, (I’m using version 20100715), and extract the /examples/hid-custom-rq directory somewhere.

Let’s get the firmware working first.  The AVR stick works a bit differently to the circuit the demo is for, so we need a few changes.

The ATmega85 only has one port: port B.  The sample uses port D.  Edit these lines in /firmware/usbconfig.h:

#define USB_CFG_IOPORTNAME      B
#define USB_CFG_DMINUS_BIT      0

The LED is on PORTB1, not PORTB0, so change /firmware/main.c:

#define LED_BIT             1

Finally, we need to change the Makefile to match the ATmega85:

DEVICE  = attiny85
F_CPU   = 16500000

Where does 16500000 come from? The AVR stick uses the internal RC oscillator for its clock, which runs at about 8MHz.  The problem is, V-USB needs a clock speed of at least 12MHz to run.  ATmega5s have a fuse setting which doubles its clock speed (they call it the PLL clock or something like that).

While the internal RC clock is fairly stable, it’s not very accurate.  When V-USB starts, it uses the USB signal to calibrate the clock so it runs at the same speed as the USB bus.  Using this calibration, it’s possible to run the internal clock at 8.25MHz, which is doubled to 16.5MHz.  This is the speed the AVR stick must run at.

Since I was using the Bus Pirate to program the AVR stick, I had to change the AVRDUDE= line also.

That was it for the firmware – hopefully it compiles and uploads to the AVR stick.

Now for the software that runs on the computer.  When I compiled and ran ./set-led on, I got this message:

Could not find USB device "LEDCtlHID" with vid=0x16c0 pid=0x5df

but when I ran lsusb, I saw this line:

Bus 002 Device 023: ID 16c0:05df VOTI

“VOTI” doesn’t match “LEDCtlHID”, no wonder it couldn’t find it!  But when I ran sudo lsusb -v, I saw this instead:

iProduct                2 LEDCtlHID

OK, so “VOTI” is simply the string form of the USB vendor ID 0x16c0, and the name “LEDCtlHID” is correct after all.  What’s going on then?

I noticed that the usbOpenDevice function takes a few file descriptors, of which the final one is called warningsFp.  This is called from set-led.c; perhaps I’ll change this last parameter from NULL to stderr.

When I ran ./set-led on again, I got this:

Warning: cannot query manufacturer for VID=0x16c0 PID=0x05df: error sending control message: Operation not permitted

That’s a slightly more useful error message!  So the USB library doesn’t seem to be able to use the device.  I read that some USB devices appear under /dev/usb, so let’s have a look with ls -l /dev/usb:

crw------- 1 root root 180, 96 2011-12-14 22:50 hiddev0
crw------- 1 root root 180, 97 2011-12-16 21:33 hiddev1

Maybe that’s the problem – there’s no write permissions on the device.  To fix this, I’ll make this device writable by everyone.  Create the file /etc/udev/rules.d/90-avrstick.rules, and put this in it:

SUBSYSTEM=="usb", DEVTYPE="usb_device", SYSFS{idVendor}=="16c0", SYSFS{idProduct}=="05df", MODE="0666"

then sudo reload udev.  Remove the stick then put it back in, then look at /dev/usb again:

crw------- 1 root root 180, 96 2011-12-14 22:50 hiddev0
crw-rw-rw- 1 root root 180, 97 2011-12-16 21:42 hiddev1

That’s a bit better.  Now run ./set-led on – the white LED on the AVR stick should come on!

There’s one improvement I can think of – the C code is pretty ugly, so I had a go in Python.  I copied the USB calls from the C code.  The Python code follows:

import sys
import usb
import itertools

vid=0x16C0; pid=0x05DF;

devs = itertools.ifilter(
    lambda dev: dev.idVendor==vid and dev.idProduct==pid,
    (dev for bus in usb.busses() for dev in bus.devices)
)         

if len(sys.argv) == 1:
    print 'Specify "on", "off" or "status"'
    sys.exit(1)
op = sys.argv[1]

for dev in devs:
    #print "Handling device %04x:%04x" % (dev.idVendor, dev.idProduct)
    handle = dev.open()
    if op == "on" or op == "off":
        handle.controlMsg(
            usb.TYPE_VENDOR | usb.RECIP_DEVICE | usb.ENDPOINT_OUT,
            1, # = CUSTOM_RQ_SET_STATUS
            [], # Empty payload - the data goes in the "wValue" field
            value = (op == "on" and 1 or 0 )
        )
    else:
        data = handle.controlMsg(
            usb.TYPE_VENDOR | usb.RECIP_DEVICE | usb.ENDPOINT_IN,
            2, # = CUSTOM_RQ_GET_STATUS
            1, # How many bytes to read
        )
        print "LED is " + (data[0] and "on" or "off")

That’s much shorter than the C code, and does the same thing (admittedly there’s much less error handling here).  I used the pyusb library version 0.4.2.  The documentation for this library is a bit thin, but I found the Python command “help(usb)” of some use, as well as the source code for the library.

Talking to the USB device is all using Control signals, which I think you need to do because it’s pretending to be an HID device.  I found it interesting that they never send data to the AVR stick – they send the desired LED status in the “wValue” field, which sounds like a 16-bit field that’s in every control message.  I don’t know much about USB, but conveniently copying the C code worked fine.

Leave a Comment