Close

RC 2022/10 Day 2: Decoding a PS/2 Keyboard With Verilog

A project log for Flounder Z180 Computer

Standalone single-board computer based on the Zilog Z180 CPU

colin-maykishColin Maykish 10/03/2022 at 02:510 Comments

I picked up where I left off yesterday by validating the RAM and hooking up one of the Z180's built-in UARTs (called ASCI0 and ASCI1). In both cases I'm just doing the bare minimum to prove out the concept. I've installed a 512KB SRAM chip, but only 16KB is currently mapped into the CPU address space. Eventually, I'd like to understand the MMU and use it address the full range of RAM, but I've got more than enough to keep moving.

Setting up the UART was done in a similarly quick-and-dirty fashion. The Z180 provides some options for deriving serial clocks, and thus baudrates, from the system clock. A common approach is to use an oscillator with a magic frequency like 18.432 MHz and then divide it down within the CPU to get a perfect 9600 or 115,200 baud. My messily-soldered prototypes are not super reliable running at 18 MHz, so my solution was to run the CPU at 1 MHz and provide a second oscillator for the serial clock. Hooking up a 1.8432 MHz to the CKA pin on the Z180 gives a 115,200 baudrate without any additional code setup.

Speaking of serial ports, I modified some portions of the bootloader/monitor I wrote for Herring to run on Flounder so I have a simple serial interface program in C that I can use to quickly test hardware and software changes.

I used this newly functional serial monitor to start work on a PS/2 keyboard interface. One of my main goals for Flounder is that it should be capable of standalone operation. User input is a big part of that. There are lots of interesting I/O methods, key pads, front panel switches, game controllers, but a PS/2 keyboard is simple and ubiquitous.

In fact, the PS/2 protocol only requires two pins: CLK and DATA. I wired up the pins to a PS/2 breakout board and to some open I/O pins on the CPLD.

Both lines also have 4.7k pullups, but these may be redundant. I think most keyboards will already have them.

Since PS/2 is a serial protocol that clocks in scan codes one bit at a time, I'm using the CPLD as a serial-to-parallel converter. The idea is that the CPLD monitors the PS/2 clock line and as it toggles, scan codes are shifted into a register on the CPLD. The Z180 can then address the CPLD like any other memory-mapped peripheral and read the scan code on its 8-bit data bus. The only downside I see to this is that it eats up a lot of CPLD pins: two for the keyboard and eight data bus lines.

I won't cover the details of the PS/2 protocol here. It's available all over the internet and it's pretty straight forward. The only trap I found was assuming that scan codes match ASCII character codes. They don't... I spent a good half hour "debugging" strange values coming in from the keyboard until it dawned on me.

Fun fact: most USB keyboards are backwards compatible with PS/2

The CPLD is running a simple state machine to decode and parse the PS/2 data. The source as it stands now is available here: https://github.com/crmaykish/flounder-z180/blob/bc4ad37afe275d4681a91163d40dbda921080ad7/hardware/flounder_cpld.v#L38

It's incomplete and not well-tested, but it is at least working. One thought: it seems tempting to use the PS/2 clock line as the clock in the Verilog modules. I think this is a trap. You'll quickly end up having to cross clock domains back to the CPU clock. I just treated the PS/2 clock as another input pin and sampled it from the CPU clock domain.

So I end the day with a proof of concept PS/2 to parallel interface. The next step is to make it a little more practical to use on the software side. I will need to come up with a way to read and acknowledge single key presses instead of constantly outputting the last read scan code. Triggering an interrupt whenever a full scan code is ready and then clearing the interrupt when the CPLD data is read by the CPU is the approach I'm going to attempt.

Discussions