XGS on the Raspberry Pi 400

About a week and half ago I became the owner of Raspberry Pi 400, a small computer built around a Raspberry Pi 4. The kit I purchased came with a USB-C power adapter and a basic USB mouse; all I needed to supply was a monitor.

Naturally, the first thing I did after setting it up was get XGS running on it:

This was not quite as simple compiling XGS and running it, so let’s go over what changed, why it changed, and how to build XGS on the Pi after the changes.

Open GL Support on the Raspberry Pi

On older Pi models (1, 2, 3, and the Zero) the OpenGL support was provided through a proprietary firmware blob, and exposed through a set of custom GL libraries in /opt/vc/lib. Since 2017 XGS supported use of these libraries by specifying -DBCMHOST=true when running cmake.

As of the Pi 4 (and, by extension, the Pi 400) this proprietary firmware driver is no longer supported. Instead, there are now open source drivers supporting the Pi 3 and 4. They are fully integrated into Mesa now, so it is no longer necessary to link against proprietary libraries.

As a result of these changes I have decided to remove the BCMHOST option in XGS. Instead, XGS relies on SDL to pick the right driver; this will be KMSDRM when running from the console, or X11 when running from the desktop.

Note that this change has probably broken support for the Pi 1 or 2. Those models are sufficiently outdated now that I don’t feel the need to continue supporting them.

Building XGS

As of this writing Raspberry Pi OS does not ship a version of SDL with the KMSDRM driver enabled. So, before building XGS, you’ll need to build and install your own build of SDL. Fortunately this is very easy:

sudo apt-get remove -y --force-yes libsdl2-dev
sudo apt-get install libgbm-dev libdrm-dev
tar -xzvf SDL2-2.0.12.tar.gz
cd SDL2-2.0.12
./configure --host=arm-raspberry-linux-gnueabihf \
--disable-pulseaudio \
--disable-esd \
--disable-video-rpi \
--enable-video-kmsdrm
make -j4
sudo make install

You should also run raspi-config, navigate to Advanced Options -> GL Driver, and make sure you have the KMS driver selected. You’ll need to reboot after making this change.

With SDL built and the KMS driver enabled you can now build XGS:

cd xgs
mkdir build
cd build
cmake ..
make -j4

The resulting binary can be run from the desktop or direct from the console. It will open in a window when run from the desktop, and full-screen when run from the console.

The only problem I have noticed is that the video is a bit choppy when running from the console. It’s most noticeable when watching the sliding apple on the “Check startup device” screen. It does not happen when running from the desktop, so it is somehow related to the KMSDRM driver.

Raspberry Pi & the Future of XGS

XGS has certainly had a fragmented development history. I originally started it in 1996, but by 1997 development had already stalled. Five years later, in 2002, I briefly resumed work on it, but I also started a small business around that time and XGS ended up on a (virtual) shelf for nearly 15 years.

Fast forward to 2016, when I got the XGS itch again. I rewrote the code in C++ to make it easier to understand, removed non-SDL graphics support, and added a basic GUI.

By 2017 I had moved away from the project again despite making good progress. I briefly considered creating a retro emulated GS based around the Raspberry Pi (think of the C64 Mini) but that project never really got off of the ground, and by that point I was already spending the bulk of my time on other projects.

And so here we are in 2020. I’m currently between hardware projects (well sort of; more on that in another post) and I’m feeling the need to spend at least some of my free time on a software project. So, I am jumping back into XGS, with the intent of improving the Raspberry Pi support. My first task will be to create an ARM-optimized version of the M65816 emulation core. I’ve been wanting to learn ARM assembly, and this will be a great way to do that.

Stay tuned!

Moving on from COLE-2

Over the last few days I have been thinking a lot about COLE-2’s future, and I kept asking myself if it is worth taking this project all the way to a final build. After much debate I finally admitted to myself that it’s not.

This project is a stepping stone, part of a long-term plan to build my dream 65816 computer. Think of it as the Apple IIGS upgrade I wish Apple had produced 30 years ago. Each design iterates on the previous one until my goal is reached. Since COLE-2 is not the end goal, once I build it it will end up in the same drawer as COLE-1 when I start my next design. Thus I’ve decided not to pursue further work on this hardware design.

As a stepping stone I think this project has done very well for me. I learned how to design a 65816 system, how to program GALs and CPLDs, and even some FPGA skills. Now it’s time for me to take the next step towards my final goal.

The Joys of Programmable Logic

My next design will be built around the ULX3S, an FPGA development board based on the Lattice ECP5. Compared to the iCE40 I used for COLE-2’s video controller the ECP5 is a beast; it sports over 84,000 LUTs compared to the paltry 7,680 on the iCE40. With that level of resources available I can build the entire system inside the FPGA itself.

Nothing is 100% finalized yet, but my plan is to connect a 65816 directly to the FPGA. The FPGA will provide the CPU with all necessary resources, such as RAM, ROM, and I/O. Future iterations may even replace the CPU with a soft core inside the FPGA, which could let it reach speeds that a physical 65816 simply can’t achieve.

The beauty of this design is that I can iterate on it without changing the hardware at all. In theory this design can be anything once it’s built, even a IIGS-compatible build, just by changing the FPGA’s code. I think that level of freedom will keep me occupied for a very long time!

As a bonus this new design will be quite small. This will make it very easy for me to push it aside when I want to use my workbench for other projects for a while.

I don’t have a name for this project yet, other than “not COLE-3”. I’ll be thinking of names as I wait for my ULX3S to arrive, which should be before the end of the month. I’ll announce the name at that point.

Taming my home office, vintage hardware, and a quick COLE-2 update

This post is far less technical than my usual posts, but since I haven’t blogged in nearly 8 months I decided it was time to post something.

So, what have I been up to? Well, like most people, I’ve just been trying to adjust to pandemic life. I’ve been working from home since late March, and that is slated to continue until at least January. Working from home has been great in some ways (I’m saving a ton on gas and lunches), but it’s also been tough in others (distracting cats, lack of sunlight, and general malaise with my lunch choices at home). For the first couple of months it was very draining on me, but now I have adjusted and things are going much better.

Home Office Reorganization

Since I am home so much I took the opportunity to get some work done on projects around the house. One of those projects was a massive reorganization of my home office/workshop, which has been in dire need of it for many years now.

Starting conditions

My home office is not very big; it’s just a spare bedroom and is roughly 8′ x 12′. One one short wall is my actual desk, plus an old end table that pretty much is just a table for my workstation. The other short wall had some very overburdened shelves holding all my technical books, plus a self-standing equipment rack, like you may find in a data center. One of the longer walls is mostly a set of double doors, and the other has my work bench. Every inch of wall space was used, and the remaining area did not leave a lot of room to work comfortably.

New Shelving

The first thing I did was recycle about half of the books, as they were all 15-20 years out of date. The remaining books went onto some brand new shelves I installed above my desk. The old shelves then came down, and I installed two Muscle Rack shelving units along that entire wall.

The equipment rack has not had any actual rack-mountable equipment in it for at least a decade; all that remained was our MythTV/Minecraft server, the cable modem, the wifi router, a cable box, and an HD-PVR. All of that went to the new racks, and the old equipment rack went to the basement.

The end result is that I now have a ton of storage space for all my parts and equipment, and my office feels much more comfortable. I still have some work to do as far as organizing what is on the racks, but it is ten times better than before.

Vintage Hardware Collection

As you might have guessed from my SBC projects I’m a fan of vintage hardware. I am particularly found of hardware from the 1970s and 80s. Over the last 20-25 years I’ve put together a nice collection, but it has been packed away in closets. My dream has been to get it all out, cleaned up, and put on display.

With my office organized I decided to gather everything into my office and display it on my new racks. While it’s not the display I dreamed of, it’s a start. As of right now my collection consists of:

  • A ColecoVision, sans power adapter. Untested since I can’t power it up.
  • A TI-99/4A, with the speech synthesizer add-on
  • A TurboExpress handheld
  • A late-model Commodore 64, but no floppy drive
  • A ROM 03 Apple IIGS, with a 3.5″ and a 5.25″ drive, but no monitor
  • A Macintosh IIfx (won’t turn on)
  • A Macintosh Quadra 660AV (also won’t turn on)
  • A Macintosh Quadra 630
  • A Macintosh SE/30
  • An SGI Indigo. Turns on, but no video
  • A Sun SPARCstation IPX. Turns on, but no video
  • A Sun SPARCstation 20. Turns on, but no video
  • A pizza box form factor NeXT workstation. Turns on, but no video

I plan to do some future blog posts about my collection, including repair work and efforts to make them more compatible with the modern world, such as adding SD card adapters where possible.

COLE-2

I’d like to end this post with a quick update on COLE-2. I have gotten some work in on this project over the summer, and my focus has been on finalizing the hardware so I can concentrate on the software. I’ll post more about those efforts soon.

New Year’s Update

Happy New Year to everyone reading this much belated status update! I apologize for the radio silence, but I took a break from this project over the summer. I came back to it en force in late November, taking advantage of my holiday time off to really sink my teeth into it again. Quite a few things changed, so let’s dive right into it.

It’s Alive….Alive!

My first goal when coming back to the project was to get the new build booting to a serial console. To make this easier I threw together a bus analyzer using a Raspberry Pi and some MCP23017 I2C I/O expanders that I had in my parts bin. With two of them, plus some Python code, I was able to have the RPi monitor the CPU buses while pulsing the system clock. This severely cut down the time it took for me to find the various wiring mistakes, and it was useful enough that I hope to someday create a more permanent version of it for future builds.

At this point I had a booting system, but it was not very stable. I partly solved this by switching (no pun intended) to a better bench power supply. The big change, though, was getting rid of all of the discrete logic, which brings us to…

The COLE System Controller

The CSC is the core of the system and is responsible for managing the system buses. It’s implemented on a Xilinx XC9572XL CPLD, and it’s responsible for:

  • Bank address latching
  • High address line generation (A16-A18)
  • Chip selection for RAM, ROM, and I/O
  • IRQ aggregation

In the previous build, the bank address was latched by a 74ACT73, which remained open (transparent) during Φ2 low, but closed and latched the bank address on the Φ2 rising edge. This is the design recommended by WDC in their application notes for the 65816.

When I first implemented this design in the CSC it made the system even more unstable. However, by switching my design to latch the bank address on the Φ2 rising edge the system suddenly became rock solid I don’t know the reason for this, but since it works I am going to leave it as-is. The only real downside to this design is that my address decoding is not as fast as it could be, and thus I would need faster RAM and ROM for any given clock speed.

TIVI

With the system booting reliably I next turned my attention to getting video into a working state again. The new video controller is called the TIVI, or (TI)ny (V)ideo (I)nterface, and is implemented on a TinyFPGA.

The TinyFPGA is a wonderful and inexpensive piece of hardware, but it does suffer from one major problem: a lack of user-accessible I/O pins. There are far too few pins available to connect it to any sort of external RAM. Fortunately, the TinyFPGA has 16 KB of on-chip dual-port block RAM. which is just enough for text and basic graphics.

Since the video RAM is on-chip the TIVI chip operates like many old-school CRTC chips, and provides registers for reading and writing video RAM. This isn’t as bad as it sounds; through the use of an auto-incrementing address register the CPU can read/write contiguous chunks of video RAM at full speed.

The output from the TIVI is an analog VGA signal with a resolution of 640×400 pixels, at a refresh rate of 85 Hz. I chose this mode because it uses a 31.5 MHz pixel clock, which the TinyFPGA’s PLL can synthesize exactly. Standard 640×480, by contrast, uses a 25.175 MHz clock, but the closest value the PLL can generate is 25 MHz. While it’s close, and it sort of works with my LCD monitor, it is technically out of spec.

Due to the small amount of available VRAM the TIVI can’t output an actual 640×400 image. The current design implements two video modes:

  • 80×25 text, with 8 background and 16 foreground colors, hardware blink, a programmable hardware cursor, and a programmable font.
  • 160×100 graphics in 6-bit color.

The text mode uses an 8×16 font stored in the high 4K of VRAM. The BIOS loads the font from ROM at startup, and when switching from graphics mode back to text mode.

Down the road I would like to try adding hardware scrolling, at least for text mode. I may also add a higher res mode such as 320×200, but this is not a priority for me.

The Speed Force

In this new build the TIVI generates the system clock by dividing its 63 MHz master clock by a programmable divisor. The default value is 24, which produces a system clock of 63/((24+1)*2) = 63/50 = 1.26 MHz. I chose this value because breadboard builds aren’t the best for grounding and noise, and 1.26 MHz is an easily attainable target in these conditions.

With the TIVI chip now accessible by the CPU I was able to twiddle the divisor after boot and see how fast I could push the system. So far I have had good success running with a divisor as low as 4, which equates to a system clock of 6.3 MHz! This exceeds my original design goal by a whopping 20%. It may be able to run a bit faster, but the next lower divisor produces 7.87 MHz, which instantly freezes the system.

The COLE Input Controller

The CIC is an ATmega 328p that handles all user input, including the keyboard, the mouse, and both game pads. It connects to the rest of the system via SPI. It’s similar to the 8042 keyboard controller in IBM PCs; it handles the low-level communications with devices, freeing the CPU for other tasks. It also manages the system’s RESET and NMI signals. This includes the initial power-on reset function previously handled by a dedicated IC.

Unlike my previous build the CIC does not do PS/2 scan code conversions, nor does it manage the LEDs automatically. Instead, raw PS/2 scan codes are fed to the CPU, which is now responsible for key mapping and LED management. It does, however, watch for some special key sequence and act upon them. Pressing Ctrl-Alt-Delete will reset the system, and Ctrl-Alt-Break will toggle the NMI line. Since the CIC handles these directly they are guaranteed to work even when the rest of the system is non-responsive.

The SPI/65B

My original build used two 6522 VIAs. One was dedicated to receiving data from the CIC, plus a bit-banged SPI implementation. The second VIA was reserved solely to the user port. There were a couple of problems with this design, however.

First, there was no way to send data to the CIC. This meant the BIOS couldn’t change key repeat rates, mouse resolution, or the keyboard LED states.

Second, the bit-banged SPI was really slow, and very CPU intensive.

Fortunately for me I stumbled upon André Fachat’s SPI/65B project, a VHDL rewrite of Daryl Rictor’s 65SPI hardware SPI controller. Daryl’s original design is wonderful, but it is written in ABEL, which is no longer supported by the Xilinx tools. André rewrote Daryl’s implementation in VHDL, with a few bug fixes and enhancements.

I was able to upload André’s design to an extra CPLD, and after a few minor tweaks I finally had working hardware SPI. This has now replaced one of the VIAs in my build, leaving just the user port VIA.

For maximum compatibility the SPI interface is running at 5V via a small level shifter. The CPLD itself is 5V tolerant, so the level shifting is only necessary on the output signals.

Now What?

With the final hardware design finally coming together, it’s almost time to start work on a PC board design. I would like get the first (and hopefully only) PCB design done and built by this spring.

There’s a lot of hard work ahead, but I’m looking forward to it!

Rebuild Update

All of the 3.3V parts for the new COLE-2 prototype have arrived, and I’ve begun the slow process of rewiring everything. I am hopeful that this rebuild will eliminate my stability problems from the previous version, and also possibly get up to a target speed of 6.25 MHz.

In addition to increased stability, this rebuild is allowing me to reconcile what I have built vs what I have drawn on the schematics. Up to this point the design in the schematics has been lagging the actual hardware, because I was making so many design changes. This time around I’m doing things right: schematic first, then build.

Before I started any wiring I experimented with various breadboard layouts and chip positioning. In the end I opted for six breadboards, oriented vertically and arranged left to right. This, plus some careful chip placement will make the wiring of the data and address lines much easier. And, with six full boards, I can now fit all of the planned features into this build and still have plenty of room left.

Here’s what the build looks like at the moment, with all of the power rails connected and about half of the data bus wiring done:

The six boards, from left to right, are:

  1. The TinyFPA and VGA connection. I’ll be adding some 74LVC245 buffers here for the address & data bus connections soon.
  2. The XC9572XL CPLD, which is replacing both of the GALs from the previous build.
  3. The 65816 CPU (bottom), plus the 74LVC245 data bus buffer (top) and the 74LVC573 bank address latch (middle).
  4. The first 512 KB static RAM (top) and VIA #1 (bottom)
  5. The second 512 KB static RAM (top), VIA #2 (middle), and the ATmega328p (bottom; this may move)
  6. The 512 KB flash ROM (top) and the 28L92 dual UART (bottom)

I don’t expect the rebuild to be done for at least a couple of weeks. I have learned that if I speed up the build I get impatient and end up doing sloppier work. So, I’m going to just do a little bit of wiring every day while I work on more interesting things, like the new VGA implementation. I think the end result will be much better off that way.

A Quick COLE-2 Status Update

It’s summer, and a particularly brutal one here in Michigan. My home office is not air conditioned, so I’ve been working on my projects a lot less. I have not been totally idle however, and so I’ve decided to give a brief update.

“I know engineers; they love to change things!”

Dr. McCoy, Star Trek: The Motion Picture

Video (Again)

Over the past few weeks I’ve grown very dissatisfied with the responsiveness of my current design. In fact, output to the VGA console is noticeably more sluggish than output to the serial port at 38.4k bps! This is largely due to the very slow interface between the CPU and the Propeller. I debated various solutions to this problem, and decided to re-implement the video and sound on a TinyFPGA.

You may recall that I’ve already dabbled with a custom VGA chip, and even had a partial prototype implemented on an Altera MAX V. The iCE40 chip on the TinyFPGA does not have a nearly as many I/O pins as the MAX V however. and so interfacing an external frame buffer is not feasible. What the chip does have, however, is 16 KB of dual-port block RAM directly on the chip. This simplifies the design considerably, at the expense of frame buffer size.

With only 16KB for the frame buffer, the graphics output will be less ambitious than my original goal. It will still have the 80×25 text mode, but graphics will be restricted to 160×120 in 16 colors. I may also add a 320×240 monochrome mode or a 320×200 4-color mode, depending on available logic resources (I do need some resources to implement sound at some point).

Finally, the new design will map the frame buffer directly into CPU memory, and run at full CPU bus speed. This will greatly improve the console’s performance, especially for scrolling.

Prototype Rebuild

The current prototype build is no longer as stable as it once was; the system will randomly reboot after an indeterminate amount of time. I have not spent much time trying to diagnose the issue, but I suspect some problem with noise on the reset line. Instead, I’ve decided to just tear the prototype down and rebuild it. The current build has had so much grafted onto it that the layout and wiring are an absolute mess, so this will be a great opportunity to clean things up and hopefully make the build more stable.

While I am rebuilding I will also be swapping out the GALs and one or two other pieces of discrete logic with a Lattice XC9572XL CPLD. This part comes in 44-pin QFP, which is still relatively easy to hand solder, and can also be easily adapted to a breadboard build. It’s also programmable from Linux with a Bus Pirate and a bit of work, so I can program the chip from the same machine where I do my other development work.

Since the XC9572XL is a 3.3V part I will need to drop the system voltage from 5V to 3.3V. This is actually going to make life easier, as I’ll no longer need logic level conversion to talk to the FPGA. It will still get me a maximum of 8 MHz, which matches up well with the speeds of my RAMs and flash ROM anyway.

Speaking of flash ROM, the one part of my build that is not 3.3V-compatible is the EEPROM. As it turns out, 3.3V parallel EEPROMs are not very plentiful, and don’t come in speeds faster than 200ns. So, for the new build, I’m going to switch to a flash ROM, specifically the SST39LF040. This will give the new build a whopping 512 KB of ROM operating at 70ns, which will be good to at least 7.1 MHz, and possibly the full 8 MHz.

The CPLD and flash ROM should be arriving later this week, so I am hoping to do some if not all of the rebuild over the weekend. Once the new build is done I’ll post another update with some pictures.

The Software Stack

Now that the hardware is mostly finalized time to talk about the software stack. My design is modeled after CP/M (and by extension, MS-DOS), in which there is a ROM BIOS that provides a standardized view of the hardware. On top of that is the actual operating system, which uses the BIOS to access the hardware and provides more complex services like working with files and launching applications.

The “official” OS for this project will of course be a version of JR/OS from COLE-1. My hope is to make JR/OS easily portable to future boards (yes, COLE-3 is already in my head!) and possibly other peoples’ boards, such as the Neon816 or the Commander X16.

With that out of the way let’s start at the bottom of the stack and talk a bit about the BIOS.

The BIOS

The BIOS serves three main functions:

  1. Initializing the hardware at power-on or reset
  2. Providing standardized APIs for accessing the hardware.
  3. Booting the operating system. This may consist of passing control to a ROM-resident copy of the operating system, or booting from external media such as an SD card.

The current incarnation of the COLE-2 ROM does not contain any OS code, nor does it implement booting. In fact, there’s not even any mass storage drivers yet.

The BIOS API

The exact list of services that will be provided by the BIOS is still a moving target, but it will for sure provide the following:

  • Attaching the console to an I/O device. COLE-2 supports both VGA and serial consoles
  • Reading/writing a byte to/from the current console device
  • Reading/writing a byte to/from a character device (such as a serial port)
  • Reading/writing a block to/from a block device (such as an SD card)
  • Making device-specific calls, in the manner of Unix’s ioctl() function. Common operations such as “set baud rate” will be standardized.
  • Enumeration of available device drivers
  • Registration of new device drivers

Making API Calls

All BIOS services are accessed via the 65816 COP instruction. The COP signature byte selects the API method to call; parameters are passed in the accumulator or on the stack. For example, here is how you would print the letter “A” to the console:

LDA #'A'
COP $02  ; SYS_CONSOLE_WRITE

All registers and CPU state are preserved, except the accumulator and the carry bit; they are used to pass data and error state back to the caller. System calls must be made in native mode but the registers can be in any combination of 8- or 16-bit mode.

The BIOS uses a dispatch table to transfer control to the selected API method after performing some house keeping. First, it saves all CPU registers onto the stack. Next, it sets the data bank register to the system bank (currently $00), and re-enables interrupts if they were enabled when COP was called (since COP disables them). It then sets the processor to 8-bit mode, sets the direct page to point to the bottom of the system call stack frame, and calls the API method using a JSL. Upon return from the API method, the BIOS will clean up any stack parameters, restore registers, and return control to the caller.

JR/OS

For those who never followed the brief history of COLE-1, JR/OS stands for Josh’s Retro Operating System. The slash in the name is an homage to GS/OS, the operating system of my teenage years.

Due to the short life of the COLE-1 project very little actual JR/OS code exists. What does exist though are some design goals:

  • FAT32 as its native file system, to make it easy to transfer files with other systems.
  • Support any block device that can provide 512-byte blocks
  • Boot from ROM or external storage
  • API provided via COP (BIOS will reserve signature bye values for OS use)
  • Initially single-process but with multi-process, protected memory support in mind for future versions
  • System memory management
  • Command shell for launching applications

I expect this to evolve heavily once I get into writing the actual JR/OS code.

Application Support

The initial version of JR/OS will operate much like CP/M or MS-DOS in that only one application process will be running at any given time. However, even this first version will lay the groundwork for a future multi-process version. Applications will be expected to behave as if they are not the sole running program, and make use of OS services such as memory management to behave properly.

Applications will consist of one or more module files, each of which must fit within a single 64K bank. These module files will be built to run at address $0000 and will be loaded into the beginning of a free bank. Future JR/OS versions may relax this restriction by supporting relocatable object files.

For multi-module applications JR/OS will provide a module loader that loads a file and returns its load address. A given module will only be loaded once; further requests for the same file will return the address of the existing copy. This will be the basis of shared library support in a future JR/OS version.

What’s Next?

Right now I’m busy working on getting the BIOS to something resembling its final state. Basic console and serial port support are already implemented; the next step is to get the SPI and SD card drivers ported over from COLE-1. At that point I’ll be able to get to work on JR/OS itself, probably in parallel with the first actual JR/OS application: the BASIC interpreter.

Finally, a working keyboard

As promised I got the new, simplified keyboard interface working this weekend. This means that COLE-2 can now operate as a fully standalone computer; no serial console needed. Of course, it’s still not terribly useful, because the only thing in ROM is the system monitor, but it’s a good start!

Overall I am very happy with the new design. It’s more or less what I described in my last post, with one small exception. In the new version, pressing one of the toggle keys (CapsLock, NumLock, and ScrollLock) is still handled inside the AVR. When a key down on one of those keys is detected, it will toggle the LED state, and then send the BIOS either a key down or key up code, depending on the new state of the LED. In other words, as far as the BIOS is concerned, the toggle keys are just regular modifier keys, albeit ones with longer-than-normal key down times.

Mouse and game pad support are not yet implemented, though the new firmware is driving both PS/2 ports now. These features, and some improvements to support hot plug and device detection, are on my list for future development.

With the keyboard finally working I’m going to bite the bullet and start cleaning up the BIOS. Specifically, I need to nail down a formal API and drive model so that I can start working on the OS and the BASIC interpreter application. We’ll see how that goes. 🙂

Updates from AVR Land

Well it took about two weeks longer than I had planned but I finally have a PS/2 keyboard implementation!

My design places the bulk of the processing on the AVR itself; it decodes the variable-length PS/2 scan codes and translates them into single-byte key codes. The AVR sends all key-down events to the BIOS along with the modifier key state at the time. The key codes it sends are a super set of ASCII; codes with the high bit set are special keys that don’t have an ASCII equivalent (such as the Fn keys). It also maintains the keyboard LEDs when the user presses one of the toggle keys (Caps Lock, Scroll Lock, and Number Lock).

The Bad News

Unfortunately, my design is not very reliable.

The first problem is that I see odd garbage key presses occasionally. The AVR PS/2 receive code does not yet check the parity bit, so it’s quite possible these key presses are due to transmission glitches.

The bigger problem, however, are the hard freezes. After enough typing on the keyboard the system will eventually freeze until I reset the AVR. When this happens the AVR is still working; pressing any of the toggle keys such as Caps Lock will properly update the LEDs. That means the AVR is successfully receiving and decoding keys, and is able to send commands to the keyboard. The breakdown appears to be in the communication link to the rest of the system.

Back to the Drawing Board

My current implementation was an interesting experiment, but I feel like it may just be too fragile to ever be fully reliable. I need something simpler, even if it means making some sacrifices. So I’ve come up with a new design.

My new design is focusing on the keyboard, mouse, and game pads (yes, I’m moving those back to the AVR). Here’s a rough diagram:

The data link to the VIA has been reduced to a unidirectional link driven by a 74HC595 shift register. This frees up six pins and allows me to move the PS/2 clock lines onto the INT0 and INT1 lines (PD2 and PD3). These two lines give me more reliable detection of clock signal transitions. It also means there are enough free lines to move the game pad support back onto the AVR.

In this updated design the AVR will still decode the PS/2 scan codes into key codes, but it will no longer act on any of them; instead they will just be fed directly to the BIOS for further processing. Mouse data will come to the BIOS preceded by a special prefix byte, and the AVR will continuously scan the game pads and present those as key up/down codes as well.

BIOS to AVR Communication

If you’ve been paying attention you may be wondering what happens to the keyboard LEDs now, since all of the key state processing has moved to the BIOS and there’s no reverse comm channel in my diagram. I have two ideas for this; one is to use the VIA’s shift register, and the other is to use the second channel on the system UART.

Using the VIA shift register would give me a moderate-speed channel back to the AVR, over which I could then send commands to either the keyboard or mouse. I have no other plans for the shift register so this could make good use of some otherwise idle hardware. The draw back, however, is that it does not support any sort of framing or error checking so even one missed bit would throw the whole thing permanently of out sync. Since I’d be implementing the AVR side entirely in software I cannot rule out missing a bit here and there.

The UART channel is my preferred choice at the moment. I would be giving up the second serial port, but I would end up with a much more robust link. The framing bits would keep the link in sync, and the AVR has a hardware UART built-in so I wouldn’t even need to implement anything in software other than a handler for the data.

SPI

In all of this talk about the new design I’ve neglected to mention the hardware SPI functionality. I was fully prepared to sacrifice this feature in the name of simplification and reliability, but when I started thinking about hooking the AVR up to the UART I realized i can still have my SPI. Indeed this may actually be a better solution than my original design; I already have fully interrupt-driven serial support in the BIOS, and so long SD card block reads and writes can be streamed to and from the AVR entirely in the background. It’s sort of like a poor man’s DMA.

Next Steps

I’ve already implemented most of the hardware changes for this design (everything is done except the serial link). I’m also 75% of the way done with the AVR firmware changes. By this weekend I should be able to type on the keyboard again, hopefully with greater reliability than before.

AVR Integration

After several frustrating days and a lot of captures from my logic analyzer I am happy to report that I finally have the AVR integrated with the system, along with the basics of PS/2 keyboard support!

I did have to make one small sacrifice, which is to move my as-yet-unimplemented game pad support from the AVR to the VIA. This was done to free up enough pins on the AVR to let me go back to an external 16 MHz resonator, as running on the internal 8 MHz clock was proving to be a bit too slow to keep up. Game pads don’t support interrupts anyway so this is not going to be an issue at all.

How it works

To begin let me explain the actual hardware connection between the VIA and the AVR (an ATmega 328p):

The connection is a bidirectional parallel bus between port A of VIA #1 and port D of the AVR. Data transfer handshaking is handled by the VIA’s built-in handshaking support., which uses the CA1 and CA2 lines to act as Data Ready and Data Acknowledge signals. When sending, the VIA generates Data Ready pulses on CA2; the AVR acknowledges the bytes by sending Data Acknowledge pulses on CA1. Receive mode works the same way except the meaning of the CA1 and CA2 lines is flipped; note that this means CA1 is always the input and CA2 the output.

Since the VIA and AVR are sharing a bidirectional data bus it is important to make sure only one device is trying to drive the bus at any given moment. This is controlled by the VIA using the DDIR line; it is high when the VIA is driving the bus and low otherwise.

Data Transactions

Every transaction starts with a data packet from the VIA, followed by a response packet from the AVR. The packet format is very simple and consists of a packet type, payload length, and the payload:

+0Packet Type
+1Payload size, low byte first
+3 – +514
Payload (optional)

The payload, when present, can be up to 512 bytes in length; this number was chosen to allow a single SD card block to fit in a packet. The payload length can even be zero, in which case the entire packet is just three bytes.

A transaction starts when the VIA drives DDIR high; this signals that it’s about to start transmitting a packet. The VIA then transmits the packet in its entirety before pulling DDIR low again and going into receive mode. It then waits for the AVR to transmit the reply packet, after which both sides fall back to their idle state.

The AVR never sends data unless it is replying to a request from the VIA. It can, however, generate an interrupt by driving the CB1 input to the VIA low. A GET_STATUS request can then be sent to the AVR to determine the source of the interrupt.

The AVR Implementation

On the AVR side all of this is implemented as a state machine, driven almost entirely by pin change interrupts:

The only part that is not interrupt-driven is the transition from the RECEIVED state to the SENDING state. The actual packet processing is done in the AVR’s main loop, and the act of queuing the reply is what triggers that state transition.

There is one small difference between the diagram above and the actual implementation, and that is the handling of DDIR going high. In the code as implemented a DDIR low-high transition will ALWAYS transition to the RECEIVING state, regardless of the current state. I did this so that it is possible to recover easily from a botched transaction without needing to reset the AVR.

PS/2 Keyboard Support

With the VIA and AVR finally talking reliably I moved on to implementing the PS/2 keyboard driver. This was much easier and took me maybe half a day.

Like the MPU interface, the PS/2 driver is interrupt-driven, this time by the PS/2 clock signal, which is always generated by the device. I won’t take the time to diagram it here since there are quite a few examples on the net of how to do this, and my implementation is nothing special.

As it stands now I am able to send and receive bytes with a PS/2 keyboard; this means I can receive key up/down events as well as control the LEDs, configure the typematic key repeat, and other fun things. I need to implement two more pieces to make this a complete solution however:

  1. Implement interrupts when the AVR has buffered data from the keyboard
  2. Decoding of key scan codes into actual ASCII that the rest of the computer expects. I am leaning towards doing this on the ‘816 side.

These two tasks are my focus for at least the beginning of the week. I’ll post another status as soon as I’m able to type to the computer without using the serial port!