AT28C64B EEPROM programmer

26 September 2021
A natural follow-on from my experimental EEPROM circuit was to build myself an EEPROM programmer, since If i was going to make any real use of such chips I needed an automated way of flashing and dumping them. This project was supposed to be one final pre-emigration project but as things turned out events caught up with me and after much start-stop progress I finally finished the hardware and got round to writing the firmware — the PCB was designed and fabricated back in early May but I only got round to finishing off the soldering just over a week ago so remarks on design decisions are really recent guesses of why I designed the hardware the way I did.

Completed circuit

Designed and fabricated back in May this project was intended to be one final one I completed while still living overseas as I did not know how much longer I would be stuck in limbo, but things then moved ahead quicker than expected and it remained unfinished by the time I emigrated. It was only in mid-September that I got round to finishing off the hardware and writing the firmware, and hence bringing the project to completion.

Circuit design

I had originally intended to make use of a Kinetis KE Cortex-M0 but due to the world shortage of ARM cores pretty much everywhere was out of stock with lead times stretching out into 2022. Instead the circuit is based around a Microchip PIC16F1829 which is the largest chip in the sub-family of PIC16F182x microcontrollers that I am most familiar with. From the outset I decided that all of Port C should be dedicated for data lines, with Ports A & B between them supplying seven pins for setting of address lines and signalling of the EEPROM's control pins — something that would require auxilliary chips. Even with the latter it was quite a squeeze to get the required functionality and this required some pins on the auxilliary chips to be tied to Vcc or ground.

Circuit schematic

Like most of the electronic circuits I have had fabricated in the last two years the KiCAD files for this one the PCB are open-sourced in my PCB Bitbucket repository, and the firmware source code for the microcontroller is also available.

Addressing lines

Due to the limited number of pins on the microcontroller some of the EEPROM socket connections have to be driven by external serial-to-parallel chips, and I decided that the connections that would be delegated would be the address lines as I felt that control pins should remain under the direct control of the microcontroller. The 74HC595 chip was used due to its very high switching speed, and since I wanted the page part of the address to be handled separately from the lower address bits I was not able to simply chain two of these serial-to-parallel chips together. While in hindsight I probably could have run two of these chips independently the shortage of pins made me opt for using an PCF8574 I/O Expander for the memory page bits; since this chip uses open-drain outputs a bank of pull-up resisitors are also required for the address lines it controls.

Circuit schematic

The PCF8574 is more or less the only game in town when it comes to 8-bit I/O expanders, and although in this case the need for pull-up resistors is a pain this is offset by it not needing any configuration prior to use. While nowhere near as fast as the 74HC595 can be, operating at the I2C speed of 100kHz rather than the circa 25MHz the 74HC595 can have bits clocked out at, the page part of the address bus is set at the start of an EEPROM page operation and is then left unchanged.

Computer interface

I had long ago decided that an RS232 connection via some sort of RS232-USB adapter is on balance the best thing to use for interfacing a circuit with a desktop computer, so I used an on-board FTDI FT230XS converter chip. I am not sure where I got the idea from but here the USB chip is powered from the USB interface rather than the main power supply, and as a result the circuit will always be recognised by the host computer whenever it is plugged in; it also allows the rest of the circuit to be power-cycled independently from the USB interface. In the past I have had a lot of bad luck with FTDI chips in the past so I exposed the internal RS232 connection to the microcontroller via a header — this allowed me to test the USB interface in isolation and should it have failed I could still have use an external TTL-level RS232 adapter.

Circuit schematic

There is leakage of current across the internal RS232 connection which is enough to raise the circuit's Vcc to 2.2 volts when USB is connected but the main power is off. This does not seem to cause any actual problems since the voltage is low enough to cause a brown-out reset but I do notice that some values in memory survive, and this does make me wonder whether there should be an optical isolator between the USB chip and the microcontroller.

Power supply

These days it is now almost-standard in my designs to supply power via a barrel jack and have on-board power regulation, because in the longer-term it is less hassle. In the past the pricing model of PCBs made it favourable to be stingy with the real-estate and use an external regulated power supply but these days the extra 2-3 square centimeters make little if any difference to cost. I have in the past used the nominal 5-volt power supply that USB provides to directly power circuitry but it is asking for trouble — suspicions are my first-revision I2C only worked properly because it was the only device on the USB bus, and the need for regulation is why so many chips are 3.3 volts.

In-circuit serial programming

These days I almost always opt for programming via an on-board header because doing so avoids a lot of trouble compared to the alternatives of either an off-circuit programming seat or an over-chip programming clip. In some cases I have opted for a larger chip such as the 16-pin PIC16F1823 rather than its 8-pin equivilent PIC16F1822 specifically to have dedicated programming connections. Since there are not enough pins on the microcontroller for some to be dedicated to the programming interface, care has to be taken to make sure that the connections that are shared with the programming header won't have problems with the firmware flashing signals. This means not using them for things like read or write enable signals. I opted for the shared connection to be the 74HC595 serial clock and data signals.

Alternative pin assignments

Since the page data lines need to be kept constant throughout an EEPROM flash or dump the chips controlling the page and cell part of the address need to be kept under seperate control. However with only five available microcontroller pins available and a 74HC595 needing three of them, there was not enough to give two such serial-to-parallel chips their own inputs, so I resorted to using an I2C I/O expander since it needed just the two I2C signals. I could have instead used two 74HC595 chips which shared a common serial data signal and had seperate lock & load signals but for some reason at the time I designed the PCB I did not want to make use of shared signals. This is all the more odd given that I had already taken the step of tying some chip control pins to Vcc and ground but I have long forgotten what I was thinking at the time. Alternatively I could have tied the clock & load pins together for one or both of the chips and clocked out an extra junk byte, since the 74HC595 data-sheet mentions this situation and states there will not be a race condition.

List of components

The zero-force chip socket for the EEPROM chip was bought from some random E-Bay seller and while I have suspicions over its origin it is good enough for what it is intended for. Most of the other components were bought from Farnell although I think one or two may have come from Digi-Key or Mouser instead; in any case all the parts are listed in the table below. I have not listed the part numbers for the resistors and capacitors because I have long-forgotten which ones I actually used.

Pad Component Manufacturer Part number
U1 Serial-to-parallel Texas Instruments SN74HC595N
U2 I/O Expander PCF8574
U3 Microcontroller Microchip PIC16F1454
U4 Zero-force socket n/a
U5 RS232-USB converter FTDI FT230XS
U6 5-volt power regulator Texas Instruments UA78M05IDCYR
J1 6-way receptacle Multicomp 2212S-06SG-85
J2 Pin header strip 2211S-22G
J3 Mini-USB connector Wurth 65100516121
J4 Barrel jack CUI Devices PJ-102AH
D1 Through-hole Red LED Kingbright L-113IDT
R1-R7 10kΩ pull-up resisitor not recorded
R9-R10
R8 1.5kΩ protective resisitor
C1 4.7μF capacitor
C2-C3 100nF capacitor

The zero insertion force socket I obtained for attaching an EEPROM claims to be a 3M 228-3345 but looking at its build quality I am pretty certain it is a cheap knock-off. On E-Bay these things go for £1–2 whereas on the likes of Farnell/Digi-Key/Mouser they are the best part of £20 assuming they are stocked at all. These days I mostly avoid E-Bay but ZIF sockets are fairly basic components and the build quality has to get pretty bad before they becomes unuseable — the only real precision required in these devices is the PCB pin spacing.

A blown component

Since the PCF8574 I/O Expander had an indicator LED on its unused output pin, one of the first things I did as a smoke-test was adapt some I2C firmware for this circuit in order to control this LED. The first few times I made some silly mistakes such as copying the I2C setup routines for an I2C slave and getting the I2C write transaction wrong, but then there came a point where I suspected the PCF8574 itself might be faulty. Rather than go around in circles I desoldered it and then wired it up on solderless breadboard for testing with known-good hardware and firmware.

Chip testing

I popped out the suspect chip from the breadboard and replaced it with a fresh one, and the LED shown in the picture above lit as expected. While the footprint on the PCB was unoccupied I tested the LED by shorting it to ground as shown in the picture below, but fortunately it was fine. Incidentally the firmware that was still on the microcontroller successfully lit this LED once the fresh I/O Expander was installed.

Checking the LED

Given the circumstances of the time it is plausable that this circuit and/or its components got treated rougher than they should have been. It had been shuttled around in chaotic circumstances, such as having being packed into hold luggage without proper anti-static protection, and some of the soldering was done with a cheap soldering iron at a time when I was likley a bit out of practice.

Microcontroller firmware

Since Microchip PIC microcontrollers are poor targets for C code writing the firmware in assembly is a no-brainer these days. Fortunately the PIC instruction set is very good for the type of timing and pin-toggling that is required from this firmware. The PIC16F1829 is a little unusual in having two I2C modules rather than having a single for which the pins can be reassigned but otherwise the functions for I2C and RS232 were recycled from previous projects with minimal parameter tweaking, so most of the work was implementing the communications protocol and getting the signal timings right. Internally the EEPROM always writes an entire page in one go so the protocol may as well be based around handling entire pages at a time.

Split address handling

Since the whole address space of the EEPROM is 14 bits it would require 16-bit variables to store as a single parameter and I very early on decided this was not worthwhile. Rather than implemeting firmware to handle 16-bit values it is logistically easier to keep the higher page bits and lower cell address bits in seperate variables, particularly as the page bits remain the same throughout the actual EEPROM flashing and dumping processes, whereas the cell bits need to be rapidly updated. In practice the only place that actually has EEPROM addresses as a single value is the hexadecimal file format the Python-based control program uses as input and output.

Chip clocking & timing

In the past I ran microcontrollers at their fastest possible internally-generated clock speed but these days I prefer to only clock them as fast as they need to be. I normally opt for 2MHz because it is the lowest clock speed that supports I2C, and coincidentally it is also the lowest that supports an RS232 BAUD rate of 57,600. At 2MHz the instruction execution time is 2μs whereas the minimum timing requirements for both the 74HC595 and the AT28C64B are 100ns or less, so the firmware does not need any delay loops because there is ample time spent running the instruction doing the toggling. In fact the only timing requirement that needs attention paid to it is the maximum pulse gap when doing page writes.

Component checking

The first bit of firmware I put together was to try and get RS232 and I2C working, and after the PCF8574 turned out to be blown I wrote some pin-toggling test firmware to check that the 74HC595 was not also damaged. In the process I also set up a timing loop to test the accuracy of the internal clock since in the past I have seen issues with the accuracy of timer-based delays, but in this case the nominal instruction time was within the margin for error of a quick check with an oscilloscope.

Communications protocol

The protocol for the RS232 interface relies heavily on the linear memory model without which many programming tasks would be unfeasably difficult on PIC microcontrollers. For testing purposes I ended up implementing the ability to read and write individual addresses but from the start a design decision was to only deal with entire pages of EEPROM storage, and it would be a page at a time because the microcontroller does not have enough memory for the full 64kBit of EEPROM capacity. I started with writing the functions for reading/writing this buffer and later decided that I would keep these read/write commands seperate from the actual EEPROM flashing & dumping. The table below summerises the host protocol, which while simple does have some sanity-checking feedback.

Buffer access EEPROM access Direct memory
Read Write Check Dump Flash Memory load Memory store
→ ‘R → ‘W → ‘C → ‘D → ‘F → ‘L → ‘S
← ‘r ← ‘w → ‘c ← ‘d ← ‘l ← ‘l ← ‘s
→ 64 data bytes → page address → page address → page address
→ cell address
→ page address
→ cell address
→ data byte
← 64 data bytes ← ‘. ← value ← ‘. ← ‘. → data byte ← ‘.

At one point I was going to use ‘.’ and ‘!’ to indicate success and failure respectively but never used the latter due to there being no error detection. Most of the communications firmware was written before the first time I put an EEPROM seat into the socket, and since there is only a very short RS232 connection between the USB converter chip and the microcontroller I decided not to implement the check command.

The way of thinking

Writing for PIC microcontroller and in particular writing firmware in assembly requires a different way of thinking, and it took a while to get back into the right mind-set. The main things are remembering the snall snippets for things like equality checks and the various ways that the linear memory model operates, but once having got back to grips with those things actually seemed quite easy. Yes there were the odd silly mistakes but that happens with every programming language. I had forgotten how exactly things like timers worked but the data-sheets for these are very concise. Using a debugger to trace the execution of a PIC microcontroller is so painfully slow as to be impractical, so having access to ready-made RS232 routines is a great help — without using the RS232 interface as a debugging side-channel I would have had to use gpsim to make writing the communication protocol reasonably painless, but among the problems gpsim in itself brings it does not seem to support the PIC16F1829.

The firmware source code

Like the PCB design files the source code for the microcontroller firmware is also open-source, because there is little value in not releasing both the software and the hardware. Originally I started open-sourcing my firmware because I was already making large portions of it available as part of project write-ups, and soon concluded that simply putting the whole lot onto Bitbucket made things easier. However in the last twelve or so months I needed a way of keeping track of what I was creating, since I was spending extended periods away from my usual desktop computer so was making stuff on “temporary” systems, and it was path of least resistance to just use my existing public repositories rather than keep public & private ones. In practice stuff that I have made public has tended to be more organised and easier to find whereas stuff I have kept private I have often lost track of.

The final sentiment

This time last year doing electronics was the only thing keeping me from losing my sanity and such projects were pretty much the only good thing I remember from an otherwise disastrous time of my life. Doing without such projects during the times I was actually overseas earlier this year was a huge sacrifice, and this project was essentialy a kit project for a time when I was without my usual equipment and component stocks, and being able to bring this one to completion is another step back towards some form of normality.