HPAK USB-GPIB adapter 82357B

intro #

[*** Note : I consider this project a dead end *** ]

These USB-GPIB adapters are based on the venerable cy7c68013 “FX” usb interface IC. This contains an 8051 core that executes code from builtin RAM; this RAM must be initialized somehow. Usually this is handled by letting the FX enumerate with its default functionality; then have some sort of hook on the PC that runs e.g. fxload to transfer the firmware to RAM, then reset + re-enumerate with this new ‘user’ firwmare.

Another way is to have an external EEPROM on the board and let the FX load its firmware from there on boot.

baked-in firwmare #

The normal usb-load process works mostly well, but sometimes glitches happen when the host goes into standby, etc. I figured why not bake the firmware into the eeprom that is already on the PCB ?

Not enough EEPROM #

My 82357B hardware only has a 24c08 eeprom (1kB), but the firmware takes 6.7kB. That’s fine, just replace with 24c64/24c128 instead ? A few issues :

address bits #

Larger eeproms need 2 address bytes to address the full mem space. On smaller devices, they can get away with 1 address byte by shoving the few extra bits in the initial ‘command’ byte, e.g.

24C16 (need 11 bits to address entire space):
    byte 0 = select code; 1 0 1 0  A10 A9 A8 R/!W
    byte 1 = address A7...A0

24c128 :
    byte 0 = select code; 1 0 1 0 e2 e1 e0 R/!W  (e2...e0 are the external ID pins)
    byte 1 = address A15...A8
    byte 2 = address A7...)

This matters to the FX because at the early boot stage, it needs to know how to access the eeprom; this is done by tying some of the E* pins high or low. In other words, the FX is hardcoded to first try a small eeprom at a certain i2c address; then a large eeprom at a different i2c address.

Also, the firmware will need some minor tweaks to accomodate this, since it reads some data from eeprom.

eeprom contents #

What does the original eep contain anyway ?

C0 57 09 18 05 00 00 00 00 55 4D 59 xx xx xx xx yy yy yy yy AA <0xFF padding>....

The first 8 bytes configure the USB VID:PID as per FX docs. The chunk starting with 0x55 is the serial# of the unit, e.g. “MY473….” I think it can also be “USxxxxxxx”…

To configure the FX so it retrieves firwmare from the eeprom, the first byte needs to be changed to 0xC2 (see TRM section 3.4.3); the VID:PID bytes can stay there, but we run into problems after that:

Table 3-6. ‘C2 Load’ Format
EEPROM Address  Contents
0 0xC2
1 Vendor ID (VID) L
2 Vendor ID (VID) H
3 Product ID (PID) L
4 Product ID (PID) H
5 Device ID (DID) L
6 Device ID (DID) H
7 Configuration byte
8 Length H
9 Length L
10 Start Address H
11 Start Address L
--- Data Block --- 
.... more data blocks with Length, Start Address etc.
--- trailing signature 80 01 E6 00 00 00 00 00

The way that works is like a srec or hex file, where chunks get copied to specified addresses. The raw bytes of our serial number would thus be (mis)interpreted as a data block.

The length field at offsets 8-9, would now hold the value 0x0055, and start_address 0x4d59. In effect copying 0x55 bytes of bogus data to an invalid address: the FX maps addresses 0x4000 to 0xe000 to ‘off-chip memory’. This is meant to be used with external parallel memory; it’s not clear how the FX would behave when asked to write there, and if that would interfere with anything else on the PCB? The 128-pin package does have dedicated pins for addr/data bus, but clearly some pins (at least D0-D4, A8-10,) are wired to something.

It may be safer to just patch the firmware to relocate the SN# elsewhere; it needs to be patched anyway to support the ‘big’ eeprom with 2-byte address.

fw patch #

A few tasks :

  • modify i2c functions that sent a 1-byte address, to now send 2 bytes; hardcode the upper byte to 0 to keep things simple
  • relocate the serial number chunk slightly : hide it in a dummy block that gets boot-copied to unused (but valid) RAM.
  • modify references to that chunk

The firmware seems to reuse always the same function at 0x18e9 to initiate a transaction and send the eeprom address:

void i2c_start_write_addr(byte param_1) {
  byte bVar1;
  byte *pbVar3;
  
  pbVar3 = i2c_start();
                    /* devselect A0: write */
  *pbVar3 = 0xa0;
  do {
    bVar1 = I2CS;
                    /* wait DONE */
  } while ((bVar1 & 1) == 0);
  I2DAT = BANK0_R7; // LSB of address is passed in bank0_R7
  do {
    bVar2 = I2CS;
                    /* wait DONE */
  } while ((bVar2 & 1) == 0);
  return;
}

The hook strategy would be to insert code before we write I2DAT, to first write I2DAT=addr_MSB (0).

In this case, I choose to change this instruction occupying 3 bytes,

CODE:18f8   MOV           DPTR,#IDAT

with a long jump to my added code (which I will place in some arbitrary unused RAM area). To return to the original code I need to restore DPTR then LJMP back :

	mov	DPTR, #I2DAT
	ljmp	POST_HOOK

Patching steps #

preparation #

  • make sure ’large’ eeprom (8 or 16kB) is installed
  • pull eeprom’s “A2” pin high with smallish (i.e. 100R) resistor
  • now the device should enumerate as a blank FX2 board, 04B4:8613
  • use https://github.com/GlasgowEmbedded/libfx2 for the next few steps

first eeprom test #

# make sure everything works : eeprom should be blank, all 0xff
fx2tool  -d 0x4b4:0x8613 -B read_eeprom 0 16

# write modified eeprom-block (i.e. no firmware, just intermediate VID+PID, and modified SN# location)
fx2tool  -d 0x4b4:0x8613 -B write_eeprom -f eep_patched.bin

# I had trouble with this, also tried without -B; in the end had to physically disc+reconnect
fx2tool  -d 0x4b4:0x8613 -B reenumerate

# here, needed to add a udev rule to adjust permissions on the new VID+PID, and fix the 'cannot access device' error

# try patched firmware (run from RAM) to see if the patches work with the displaced SN#:
# hm, no success
fx2tool  -d 0x0957:0x0718 -B write_xram -f fw_patched_p_fw18_01_eeprom.hex

# what about fxload ?
fxload -t fx2 -d 0x957:0x718 -vv -i fw_patched_p_fw18_01_eeprom.hex

# Ok, it enumerates with the good VID:PID and apparently descriptors but can we still load code to RAM ? docs were unclear whether this is possible with 'user firmware' running

post-mortem #

In the end I wasn’t able to get this to run satisfactorily. Enumeration was fine, I was executing at least some normal user code; sometimes I got the serial number to appear in the descriptors, but it was still unusable.

One interesting thing that wasn’t clear from FX docs, is that even when running user firmware booted from eeprom, the FX still responds to a few special USB requests which usually means it’s possible to recover from buggy firmware, without needing to short pins and power-cycle.

If anyone wants to waste time on this, here’s my patch so far

Calendar Last update: April 19, 2026