AT28C256-rpi-util/eeprom.py
2023-10-26 22:02:19 +02:00

316 lines
10 KiB
Python
Executable File

#!/bin/env python3
from sys import exit
from time import sleep
from RPi.GPIO import IN, OUT
import RPi.GPIO as GPIO
from re import fullmatch
import argparse
import atexit
# EEPROM AT28C256 Pin names and RPi GPIO Pin Numbers
# b means bar = inverted
gpio_l = [2, 3, 4, 17, 27, 22, 10, 9, 11, 5, 6, 13, 19, 26]
gpio_r = [14, 15, 18, 23, 24, 25, 8, 7, 12, 16, 20, 21]
#
# Defining which 6502 pin goes to which GPIO pin
#
A = [
# 27, 22, 10, 9, 11, 5, 6, 13,
13, 6, 5, 11, 9, 10, 22, 27, # A0 - A7
2, 3, 4, 17, # A8 - A11
23, 18, 15 # A12 - A14
]
# IO = gpio_r[4:12] # 8 io pins
# IO.reverse()
IO = [ 24, 25, 8, 7, 12, 16, 20, 21 ]
OEb = 26 # Output Enable
WEb = 19 # Write Enable
CEb = 14 # Chip Enable is hooked up to A15 on the processor
controls = [CEb, WEb, OEb]
# TIMES
# Read:
t_ACC = 150 * 1e-9 # Address to Output Delay
# Write:
t_AS = 0 # Address Setup time
t_AH = 50 * 1e-9 # Address Hold Time
t_CS = 0 # Chip Select Hold Time
t_WP = 100 * 1e-9 # Write Pulse Width
t_DS = 50 * 1e-9 # Data Setup Time_CS = 0
t_DH = 0 # Data Hold Time
t_WPH = 50 * 1e-9 # Write Puls High
# t_WPH = 50 * 1e-4 # Write Pulse High !!!2*e5 longer than in Datasheet, since shorter high caused Problems with my Chip!!!
def setup_address_control_pins():
# setup the pins
GPIO.setmode(GPIO.BCM)
for pin in controls:
GPIO.setup(pin, OUT, initial=1) # inverted, is 1 means disable
for pin in A:
GPIO.setup(pin, OUT, initial=0)
def setup_io_pins(IOdirection=OUT):
# OUT when writing and IN when reading
for pin in IO:
GPIO.setup(pin, IOdirection)
def cleanup():
GPIO.cleanup()
def print_pins():
for i in range(len(A)):
print(f" A{i:02} - {A[i]}")
for i in range(len(IO)):
print(f"IO{i:02} - {IO[i]}")
print(f" CEb - {CEb}")
print(f" WEb - {WEb}")
print(f" OEb - {OEb}")
def set_address(address: int, bits=8):
"""
set the address pins to the given value
"""
ad_bin = format(address, f"0{bits}b") # get the x-bit verion if the address, eg 12 -> 00001100
for j in range(bits):
# print("Address:", address, ad_bin, j)
if ad_bin[bits-1-j] == "0":
GPIO.output(A[j], 0)
elif ad_bin[bits-1-j] == "1":
GPIO.output(A[j], 1)
return ad_bin
def get_bits(i: int):
"""
return how many bits are needed to express the number in binary
"""
return len(bin(i)) - 2 # -2 for the "0x"
def convert_to_int(v):
"""
convert 0xABC and 0b0101 to int
"""
if type(v) == str and fullmatch(f"0x[0-9,a-f,A-F]+", v):
return int(v.replace("0x", ""), 16)
elif type(v) == str and fullmatch(f"0b[0]+", v):
return int(v.replace("0b", ""), 2)
return int(v)
def get_8_bit(l: list):
for i in range(len(l)):
l[i] = format(l[i], f"08b") # get the 8-bit bin value
return l
def erase(from_ad=0, to_ad=32767, **keys):
"""
Write all 1 to the EEPROM
WEb controlled
"""
data = [0xff for i in range(from_ad, to_ad)]
write(data, from_ad=from_ad, **keys)
print("Erased EEPROM - Done!")
return
def write_byte(byte, address, verbose=True):
GPIO.output(OEb, 1)
# setup the address
ad_bin = set_address(address, bits=15)
setup_io_pins(OUT)
# Setup Data
for j in range(8):
if byte[7-j] == "1":
bit = 1
else:
bit = 0
GPIO.output(IO[j], bit)
# wait "Address" Setup Time
sleep(t_AS)
# wait "Data Setup Time"
sleep(t_DS)
GPIO.output(CEb, 0)
# Start the write pulse -> enable WEb
GPIO.output(WEb, 0)
# wait until minimum write pulse width is reached. in theory, should be t_WP-t_DS but this caused problems
sleep(t_WP)
# End Write Pulse -> disable WEb
GPIO.output(WEb, 1)
GPIO.output(CEb, 1)
# wait "Data Hold"
sleep(t_DH)
GPIO.output(CEb, 0)
GPIO.cleanup(IO)
setup_io_pins(IN)
# check the toggle bit IO6, if it stops toggling the write is done
timeout = 0
while timeout < 1e3:
GPIO.output(OEb, 0)
sleep(1e-9)
bit1 = GPIO.input(IO[6])
GPIO.output(OEb, 1)
sleep(1e-9)
GPIO.output(OEb, 0)
sleep(1e-9)
bit2 = GPIO.input(IO[6])
GPIO.output(OEb, 1)
sleep(1e-9)
if bit1 == bit2:
timeout = 1e3
timeout += 1
GPIO.output(CEb, 1)
if verbose:
print(f"Writing:\t0b{format(address, '015b')} - 0b{byte} ||| 0x{format(address, '04x')} - {hex(int(byte, 2))}")
def read_byte(address):
GPIO.output(WEb, 1)
setup_io_pins(IN)
# set the address valid
ad_bin = set_address(address, bits=15)
# low in chip/output enable -> enable
GPIO.output(CEb, 0)
GPIO.output(OEb, 0)
# wait the "Address to Output Delay" until the output is valid
sleep(t_ACC)
byte = ""
for j in range(8):
if GPIO.input(IO[7-j]) == 1:
byte += "1"
else:
byte += "0"
# high in OEb and CEb -> disable
GPIO.output(OEb, 1)
GPIO.output(CEb, 1)
return int(byte, 2)
def read(from_ad=0, to_ad=255, delay=1e-3, ignore=[0xff], verbose=True, single_step=False, compare=None):
"""
from_ad: start address from where to read
to_ad: end address to read to
delay: delay between readings in s
verbose wether to print the reading
ignore list of values which are not printed
"""
content = []
unequal = []
for i in range(from_ad, to_ad + 1):
byte = read_byte(i)
content.append(byte)
if not compare and verbose and not byte in ignore:
print(f"Reading:\t0b{format(i, '015b')} - 0b{format(byte, '08b')} ||| 0x{format(i, '04x')} - 0x{format(byte, '02x')}")
elif compare:
if not compare[i] == byte:
unequal.append(i)
print(f"Unequal at Address 0x{format(i, '04x')} ||| File: 0x{format(compare[i], '02x')} vs EEPROM: 0x{format(byte, '02x')}")
# wait artifical delay
sleep(delay)
if single_step:
input("Press Return to read the next byte")
if compare:
return unequal
return content
def write(content: list, from_ad=0, delay=0, single_step=False, verbose=True, check_written=True):
"""
Write a list if bytes to the eeprom.
WEb controlled
"""
or_content = content.copy()
content = get_8_bit(content)
failed = []
print(f"Writing to EEPROM: {len(content)} bytes from address {hex(from_ad)}.")
for i in range(len(content)):
write_byte(content[i], from_ad + i, verbose=verbose)
# wait artifical delay
sleep(delay)
if single_step:
input("Press Return to write the next byte")
print("Write to EEPROM - Done!")
if check_written:
print("Comparing EEPROM to file...")
failed = read(from_ad=from_ad, delay=delay, single_step=single_step, verbose=False, compare=or_content)
while len(failed) > 0:
for ad in failed:
write_byte(content[ad], ad, verbose=verbose)
failed = read(from_ad=from_ad, delay=delay, single_step=single_step, verbose=False, compare=or_content)
print("Comparing complete")
return
def get_bytes(filepath, from_ad=0):
with open(filepath, "rb") as file:
bindata = []
for byte in file.read():
bindata.append(byte)
return bindata[from_ad:]
def main():
parser = argparse.ArgumentParser(
prog="AT28C256-EEPROM Writer",
description="Read and write from/to an AT28C256 EEPROM via the Raspberry Pi 4's GPIO pins\n2023 Matthias Quintern",
epilog="Numbers can be passed as int, hex prefixed with '0x' and binary prefixed with '0b'",
)
parser.add_argument("--write", "-w", dest="write", action="store_true", default=False, help="write binary file onto the EEPROM")
parser.add_argument("--compare", "-c", dest="compare", action="store_true", default=False, help="compare EEPROM content to binary file")
parser.add_argument("--read", "-r", dest="read", action="store_true", default=False, help="read the contents of the EEPROM")
parser.add_argument("--erase", "-e", dest="erase", action="store_true", default=False, help="erase the contents of the EEPROM")
parser.add_argument("--file", "-f", metavar="file", dest="file", action="store", default="", help="file for --write and --compare")
parser.add_argument("--from", metavar="start-address", dest="from_ad", action="store", default=0, type=convert_to_int, help="start at address x")
parser.add_argument("--to", metavar="end-address", dest="to_ad", action="store", default=32767, type=convert_to_int, help="end at address y (inclusive)")
parser.add_argument("--single-step", dest="single_step", action="store_true", default=False, help="single step the program")
parser.add_argument("--delay", dest="delay", metavar="delay", action="store", default=0, help="extra delay between cycles")
parser.add_argument("--ignore", dest="ignore", metavar="numbers", action="store", nargs="*", type=convert_to_int, default=[0x100], help="ignore the numbers a b ... Default=[0xff]")
parser.add_argument("--verbose", dest="verbose", action="store_true", default=False, help="increase verbosity")
parser.add_argument("--print-pins", dest="print_pins", action="store_true", default=False, help="print the pin layout and exit")
args = parser.parse_args()
if (args.write or args.compare) and not args.file:
parser.error('--file is required with --write and --compare')
if args.print_pins:
print_pins()
exit(0)
if args.erase:
erase(from_ad=args.from_ad, to_ad=args.to_ad, delay=args.delay, single_step=args.single_step, verbose=args.verbose)
if args.write:
write(get_bytes(args.file, from_ad=args.from_ad), from_ad=args.from_ad, delay=args.delay, single_step=args.single_step, verbose=args.verbose)
if args.read:
read(from_ad=args.from_ad, to_ad=args.to_ad, delay=args.delay, single_step=args.single_step, verbose=args.verbose, ignore=args.ignore)
if args.compare:
compare(from_ad=args.from_ad, to_ad=args.to_ad, delay=args.delay, single_step=args.single_step, verbose=args.verbose, ignore=args.ignore, compare=get_bytes(args.file))
if __name__ == '__main__':
atexit.register(cleanup)
setup_address_control_pins()
main()