Skip to content

Instantly share code, notes, and snippets.

@dev-zzo
Last active December 14, 2018 17:33
Show Gist options
  • Select an option

  • Save dev-zzo/9620fe33b8938ec2d6f157b39eed9fc5 to your computer and use it in GitHub Desktop.

Select an option

Save dev-zzo/9620fe33b8938ec2d6f157b39eed9fc5 to your computer and use it in GitHub Desktop.
Arduino EPROM programmer
import argparse
import serial
import struct
import sys
class CommunicationError(Exception):
pass
def ping(port):
port.write('?')
rsp = port.read(1)
if rsp != '!':
raise CommunicationError("unexpected response: '%s'" % rsp)
def read(port, address, count):
# Write command, check status
port.write('R' + struct.pack('>IB', address, count))
rsp = port.read(1)
if rsp == '!':
raise ValueError("wrong arguments/state")
elif rsp != '.':
raise CommunicationError("unexpected response: '%s'" % rsp)
# Read data
data = port.read(count)
if len(data) != count:
raise CommunicationError("unexpected data length: %d" % len(data))
# Check status
rsp = port.read(1)
if rsp != '.':
raise CommunicationError("unexpected response: '%s'" % rsp)
return data
def set_type(port, ic_type):
if ic_type == "2764":
real_type = '0'
elif ic_type == "27256":
real_type = '1'
else:
raise ValueError("unknown IC type provided")
# Write command, check status
port.write('T' + real_type)
rsp = port.read(1)
if rsp != '.':
raise CommunicationError("unexpected response: '%s'" % rsp)
def program(port, address, data):
# Write command, check status
port.write('P' + struct.pack('>IB', address, len(data)))
rsp = port.read(1)
if rsp == '!':
raise ValueError("wrong arguments/state")
elif rsp != '.':
raise CommunicationError("unexpected response: '%s'" % rsp)
# Write data, check status
port.write(data)
rsp = port.read(1)
if rsp != '.':
raise CommunicationError("unexpected response: '%s'" % rsp)
# Wait for programming status
rsp = port.read(1)
if rsp == '!':
offset, expected, verified = struct.unpack("BBB", port.read(3))
raise ValueError("programming error at address %08X: expected %02X, verified %02X" % (address+offset, expected, verified))
elif rsp != '.':
raise CommunicationError("unexpected response: '%s'" % rsp)
def program_file(port, address, fp):
while True:
data = fp.read(0x40)
if not data:
break
print("Programming %06X" % address)
program(port, address, data)
address += len(data)
def read_file(port, address, count, fp):
while count > 0:
data = read(port, address, min(0x40, count))
fp.write(data)
count -= len(data)
address += len(data)
def dump(port, count):
with open("dump.bin", "wb") as fp:
read_file(port, 0, count, fp)
if __name__ == '__main__':
port = serial.Serial("com12", baudrate=250000, timeout=5)
try:
ping(port)
print("Ping OK")
except:
pass
try:
ping(port)
print("Ping OK")
except:
pass
try:
ping(port)
print("Ping OK")
except:
pass
port.timeout = 30
set_type(port, "27256")
if False:
with open("Battle City (J) [p1].prg", "rb") as fp:
program_file(port, 0, fp)
else:
dump(port, 0x8000)
/* Arduino-based EPROM programmer
Pin assignments:
* EPROM D0~D7 => PB0,PB1,PD2~PD7
* EPROM PGM# => PC0
* EPROM CE# => PC1
* EPROM OE# => PC2
* EPROM Ax => '595 Qx
* '595 SIn => PB3/MOSI
* '595 CLK => PB5/SCK
* '595 RCLK => PB2/SS#
*/
/* General notes
* 2764 / 27128 / 27010 / 27020
- CE# stays asserted
- PGM# programs
- OE# verifies
* 27256
- CE# programs
- PGM# is absent
- OE# verifies
* 27512
- CE# programs and verifies
- OE# connected to VPP
*/
#define __AVR_ATmega328P__ 1
#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#define ADDRESS_CHAIN_SIZE 3
static void set_address(uint32_t address)
{
/* Assert SS */
PORTB &= ~_BV(PB2);
SPDR = address & 0xFF;
while (!(SPSR & _BV(SPIF)));
#if ADDRESS_CHAIN_SIZE > 1
SPDR = (address >> 8) & 0xFF;
while (!(SPSR & _BV(SPIF)));
#endif
#if ADDRESS_CHAIN_SIZE > 2
SPDR = (address >> 16) & 0xFF;
while (!(SPSR & _BV(SPIF)));
#endif
/* Deassert SS */
PORTB |= _BV(PB2);
}
static void assert_PGM(void)
{
PORTC &= ~_BV(PC0);
}
static void deassert_PGM(void)
{
PORTC |= _BV(PC0);
}
static void assert_CE(void)
{
PORTC &= ~_BV(PC1);
}
static void deassert_CE(void)
{
PORTC |= _BV(PC1);
}
static void assert_OE(void)
{
PORTC &= ~_BV(PC2);
}
static void deassert_OE(void)
{
PORTC |= _BV(PC2);
}
static void set_Dx_inputs(void)
{
/* Enable pull-up */
PORTD |= 0xFC;
PORTB |= 0x03;
/* Set to input */
DDRD &= 0x03;
DDRB &= 0xFC;
}
static void set_Dx_outputs(void)
{
/* Set to output */
DDRD |= 0xFC;
DDRB |= 0x03;
}
static void write_Dx(uint8_t value)
{
PORTD = (PORTD & 0x03) | (value & 0xFC);
PORTB = (PORTB & 0xFC) | (value & 0x03);
}
static uint8_t read_Dx(void)
{
return (PIND & 0xFC) | (PINB & 0x03);
}
/* 16M = 62.5 ns */
static void read(uint32_t address, uint8_t *bytes, uint8_t count)
{
set_Dx_inputs();
assert_CE();
assert_OE();
while (count--) {
set_address(address++);
/* tACC = 250 ns max */
__asm__ __volatile__("nop; nop; nop; nop;"::);
__asm__ __volatile__("nop; nop; nop; nop;"::);
*bytes++ = read_Dx();
}
deassert_OE();
deassert_CE();
}
typedef uint8_t (*program_func_t)(uint32_t address, const uint8_t *bytes, uint8_t count);
static uint8_t fail_index, expected, verified;
static uint8_t program_2764(uint32_t address, const uint8_t *bytes, uint8_t count)
{
uint8_t index;
uint8_t try_count;
deassert_OE();
assert_CE();
index = 0;
while (count--) {
uint8_t byte_written = bytes[index];
uint8_t byte_read;
set_address(address);
try_count = 15;
do {
/* OE float delay */
__asm__ __volatile__("nop;"::);
set_Dx_outputs();
write_Dx(byte_written);
/* tDS = 2 us min */
_delay_us(2.0);
/* tPW = 100 us */
assert_PGM();
switch (try_count) {
case 25: _delay_ms(1.0); break;
case 24: _delay_ms(3.0 * 1); break;
case 23: _delay_ms(3.0 * 2); break;
case 22: _delay_ms(3.0 * 3); break;
case 21: _delay_ms(3.0 * 4); break;
case 20: _delay_ms(3.0 * 5); break;
case 19: _delay_ms(3.0 * 6); break;
case 18: _delay_ms(3.0 * 7); break;
case 17: _delay_ms(3.0 * 8); break;
case 16: _delay_ms(3.0 * 9); break;
default:
_delay_ms(3.0 * 10);
break;
}
deassert_PGM();
/* tDH = 2 us min */
_delay_us(2.0);
set_Dx_inputs();
/* tOES = 2 us min */
_delay_us(2.0);
assert_OE();
/* tOE = 100 ns max */
__asm__ __volatile__("nop; nop; nop; nop;"::);
byte_read = read_Dx();
deassert_OE();
if (byte_written == byte_read)
break;
} while (--try_count);
if (!try_count) {
fail_index = index;
expected = byte_written;
verified = byte_read;
break;
}
/* Move to next address */
address++;
index++;
}
deassert_CE();
deassert_OE();
return try_count != 0;
}
static uint8_t program_27256(uint32_t address, const uint8_t *bytes, uint8_t count)
{
uint8_t index;
index = 0;
while (count--) {
uint8_t byte_written = bytes[index];
uint8_t byte_read;
uint8_t try_count = 25;
set_address(address);
do {
/* tDF = 130 ns max */
__asm__ __volatile__("nop;"::);
set_Dx_outputs();
write_Dx(byte_written);
/* tDS = 2 us min */
_delay_us(2.0);
/* tPW = 100 us */
assert_CE();
_delay_us(100.0);
deassert_CE();
/* tDH = 2 us min */
_delay_us(2.0);
set_Dx_inputs();
/* tOES = 2 us min */
_delay_us(2.0);
assert_OE();
/* tOE = 100 ns max */
__asm__ __volatile__("nop; nop;"::);
byte_read = read_Dx();
deassert_OE();
if (byte_written == byte_read)
break;
} while (--try_count);
if (!try_count) {
fail_index = index;
expected = byte_written;
verified = byte_read;
break;
}
/* Move to next address */
address++;
index++;
}
deassert_CE();
deassert_OE();
return try_count != 0;
}
static void hif_transmit(uint8_t value)
{
while (!(UCSR0A & _BV(UDRE0)));
UDR0 = value;
}
static uint8_t hif_receive(void)
{
while (!(UCSR0A & _BV(RXC0)));
return UDR0;
}
static void setup()
{
/* Setup pins */
set_Dx_inputs();
PORTC = 0xFF;
DDRC = _BV(PC0) | _BV(PC1) | _BV(PC2);
DDRB = _BV(PB2) | _BV(PB3) | _BV(PB5);
/* Setup SPI */
/* Master; MSB first; clock speed = fOSC/4 */
SPCR = _BV(SPE) | _BV(MSTR);
/* Setup UART */
/* Baud rate = 250k */
UBRR0L = 3;
UBRR0H = 0;
UCSR0A = 0;
UCSR0B = _BV(RXEN0) | _BV(TXEN0);
/* Asynchronous USART; 8N1 */
UCSR0C = _BV(UCSZ01) | _BV(UCSZ00);
}
static program_func_t program = 0;
static uint8_t buffer[128];
static void do_read()
{
uint32_t address;
uint8_t count;
uint8_t index;
address = (uint32_t)hif_receive() << 24;
address |= (uint32_t)hif_receive() << 16;
address |= hif_receive() << 8;
address |= hif_receive();
count = hif_receive();
if (count > sizeof(buffer)) {
hif_transmit('!');
return;
}
hif_transmit('.');
read(address, buffer, count);
for (index = 0; index < count; ++index) {
hif_transmit(buffer[index]);
}
hif_transmit('.');
}
static void do_program()
{
uint32_t address;
uint8_t count;
uint8_t index;
address = (uint32_t)hif_receive() << 24;
address |= (uint32_t)hif_receive() << 16;
address |= hif_receive() << 8;
address |= hif_receive();
count = hif_receive();
if (count > sizeof(buffer) || !program) {
hif_transmit('!');
return;
}
hif_transmit('.');
for (index = 0; index < count; ++index) {
buffer[index] = hif_receive();
}
hif_transmit('.');
if (!program(address, buffer, count)) {
hif_transmit('!');
hif_transmit(fail_index);
hif_transmit(expected);
hif_transmit(verified);
}
else {
hif_transmit('.');
}
}
static void do_set_type()
{
switch (hif_receive()) {
case '0':
program = &program_2764;
break;
case '1':
program = &program_27256;
break;
default:
program = 0;
hif_transmit('!');
return;
}
hif_transmit('.');
}
int main()
{
setup();
for (;;) {
set_Dx_inputs();
deassert_PGM();
deassert_CE();
deassert_OE();
switch (hif_receive()) {
case '?':
hif_transmit('!');
break;
case 'R':
do_read();
break;
case 'P':
do_program();
break;
case 'T':
do_set_type();
break;
}
}
}
FUSES =
{
.low = (FUSE_CKSEL3 & FUSE_SUT0 & FUSE_SUT1),
.high = HFUSE_DEFAULT,
.extended = EFUSE_DEFAULT,
};
CC=avr-gcc
OBJCOPY=avr-objcopy
OBJS=main.o
PROJECT=eprog
CFLAGS=-Os -mmcu=atmega328p
LDFLAGS=-Os -mmcu=atmega328p
.PHONY: all burn clean
all: $(PROJECT).flash.hex
$(PROJECT).flash.hex: $(PROJECT).elf
$(OBJCOPY) -R .eeprom -R .fuse -R .lock -O ihex $< $@
$(PROJECT).elf: $(OBJS)
$(CC) $(LDFLAGS) -o $@ $<
burn: $(PROJECT).flash.hex
avrdude -v -V -patmega328p -carduino -PCOM12 -b115200 -D -Uflash:w:$(PROJECT).flash.hex:i -Ulfuse:w:0xFF:m -Uhfuse:w:0xDE:m -Uefuse:w:0x05:m
clean:
del *.o
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment