AT28C256-rpi-util/eeprom.py

485 lines
16 KiB
Python
Raw Permalink Normal View History

2023-10-26 22:02:19 +02:00
#!/bin/env python3
from sys import exit
2023-10-26 19:00:17 +02:00
from time import sleep
from RPi.GPIO import IN, OUT
import RPi.GPIO as GPIO
from re import fullmatch
2023-10-26 22:02:19 +02:00
import argparse
import atexit
2023-10-26 19:00:17 +02:00
# 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]
2023-12-04 21:08:39 +01:00
2023-10-26 19:00:17 +02:00
#
# Defining which 6502 pin goes to which GPIO pin
#
2023-10-26 22:02:19 +02:00
A = [
# 27, 22, 10, 9, 11, 5, 6, 13,
13, 6, 5, 11, 9, 10, 22, 27, # A0 - A7
2023-10-28 03:47:23 +02:00
3, 4, 18, 17, # A8 - A11
23, 2, 15 # A12 - A14
2023-10-26 22:02:19 +02:00
]
2023-10-26 19:00:17 +02:00
2023-10-28 03:47:23 +02:00
IO = [ 16, 20, 21, 12, 7, 8, 25, 24]
2023-10-26 19:00:17 +02:00
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:
2023-10-28 03:47:23 +02:00
t_read_ACC = 150 * 1e-9 # Address to Output Delay
t_read_CE = 70 * 1e-9 # CE to output delay
t_read_OE = 150 * 1e-9 # OE to output delay
t_read_DF = 50 * 1e-9 # data float
2023-10-26 19:00:17 +02:00
# Write:
2023-10-28 03:47:23 +02:00
t_write_AS = 0 # Address Setup time
t_write_OES = 0 # OEb Setup time
t_write_AH = 50 * 1e-9 # Address Hold Time
t_write_CS = 0 # Chip Select Setup Time
t_write_CH = 0 # Chip Select Hold Time
t_write_WP = 100 * 1e-9 # Write Pulse Width
t_write_DS = 50 * 1e-9 # Data Setup Time
t_write_DH = 10 * 1e-9 # Data Hold Time
t_write_OEH = 0 # OEb Hold Time
t_write_WPH = 50 * 1e-9 # Write Puls High
t_write_WC = 10 * 1e-3 # Write Cycle Time
# t_write_WP = 100 * 1e-6 # Write Pulse Width
# t_write_WPH = 50 * 1e-6 # Write Pulse High !!!2*e5 longer than in Datasheet, since shorter high caused Problems with my Chip!!!
# t_read_ACC = 150 * 1e-6 # Address to Output Delay
# t_write_DH = 1 * 1e-5 # Data Hold Time
# Toggle
t_toggle_OE = 0 # OE to output delay
t_toggle_OEH = 10 * 1e-9 # OE hold time
t_toggle_OEHP = 150 * 1e-9 # OE high pulse
# t_toggle_OEH = 500 * 1e-7 # OE hold time
# t_toggle_OEHP = 500 * 1e-7 # OE high pulse
2023-10-26 19:00:17 +02:00
2023-10-27 02:41:58 +02:00
PRINT_HEX = True
PRINT_BIN = False
PRINT_DEC = False
REP_CHAR = '\n'
2023-10-26 19:00:17 +02:00
2023-10-27 02:41:58 +02:00
#
# UTILITY
#
2023-10-26 19:00:17 +02:00
def print_pins():
for i in range(len(A)):
2023-10-26 22:02:19 +02:00
print(f" A{i:02} - {A[i]}")
2023-10-26 19:00:17 +02:00
for i in range(len(IO)):
2023-10-26 22:02:19 +02:00
print(f"IO{i:02} - {IO[i]}")
print(f" CEb - {CEb}")
print(f" WEb - {WEb}")
print(f" OEb - {OEb}")
2023-10-26 19:00:17 +02:00
2023-10-27 02:41:58 +02:00
def format_number(n, hex_digits=2, bin_digits=8, dec_digits=3, sep=" "):
s = ""
if not PRINT_HEX and not PRINT_BIN and not PRINT_DEC:
print("ERROR: echo: at least one of hex, bin, dec must be set to True")
exit(1)
if PRINT_HEX: s += "0x" + format(n, f'0{hex_digits}x') + sep
if PRINT_BIN: s += "0b" + format(n, f'0{bin_digits}b') + sep
if PRINT_DEC: s += format(n, f'0{dec_digits}') + sep
return s[:-len(sep)]
2023-10-26 19:00:17 +02:00
2023-10-27 02:41:58 +02:00
def echo(address: int, value: int, message: str="", mode: str="-", end=None):
2023-10-26 19:00:17 +02:00
"""
2023-10-27 02:41:58 +02:00
print a message for a given address and value
2023-10-26 19:00:17 +02:00
"""
2023-10-27 02:41:58 +02:00
s = mode + " " "[" + format_number(address, hex_digits=4, bin_digits=15, dec_digits=5) \
+ "] {" + format_number(value, hex_digits=2, bin_digits=8, dec_digits=3) + "} " \
+ message
print(s, end=REP_CHAR if end is None else end)
def clear_line():
print(" " * 80, end='\r')
2023-10-26 19:00:17 +02:00
2023-10-26 22:02:19 +02:00
def convert_to_int(v):
2023-10-26 19:00:17 +02:00
"""
2023-10-26 22:02:19 +02:00
convert 0xABC and 0b0101 to int
2023-10-26 19:00:17 +02:00
"""
2023-10-26 22:02:19 +02:00
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)
2023-10-26 19:00:17 +02:00
2023-10-27 02:41:58 +02:00
def get_n_bits(i: int):
"""
return how many bits are needed to express the number in binary
"""
return len(bin(i)) - 2 # -2 for the "0x"
2023-10-26 19:00:17 +02:00
2023-10-26 22:02:19 +02:00
2023-10-27 02:41:58 +02:00
def get_bit(value: int, n: int):
2023-10-26 19:00:17 +02:00
"""
2023-10-27 02:41:58 +02:00
get the n-th bit (0-based)
2023-10-26 19:00:17 +02:00
"""
2023-10-27 02:41:58 +02:00
return value & (1 << n)
2023-10-26 19:00:17 +02:00
2023-10-28 03:47:23 +02:00
def read_binary_file(filepath, from_ad=0, to_ad=-1) -> list[int]:
2023-10-27 02:41:58 +02:00
with open(filepath, "rb") as file:
bindata = []
for byte in file.read():
bindata.append(byte)
2023-10-28 03:47:23 +02:00
if to_ad < 0:
to_ad = len(bindata) - 1
return bindata[from_ad:to_ad+1]
2023-10-27 02:41:58 +02:00
#
# GPIO functions
#
def setup_io_pins(IOdirection=OUT):
# OUT when writing and IN when reading
for pin in IO:
GPIO.setup(pin, IOdirection)
def setup_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)
setup_io_pins(IN)
def cleanup():
GPIO.cleanup()
def set_address(address: int, bits=15):
"""
set the address pins to the given value
"""
for j in range(bits):
GPIO.output(A[j], get_bit(address, j))
def write_byte(byte: int, address: int, verbose=True, debug=False):
if verbose: echo(address, byte, mode="w")
2023-10-26 19:00:17 +02:00
GPIO.output(OEb, 1)
# setup the address
2023-10-27 02:41:58 +02:00
set_address(address, bits=15)
2023-10-26 19:00:17 +02:00
GPIO.output(CEb, 0)
2023-10-28 03:47:23 +02:00
# wait setup time
sleep(max(t_write_CS, t_write_AS, t_write_OES))
2023-10-26 19:00:17 +02:00
# Start the write pulse -> enable WEb
GPIO.output(WEb, 0)
2023-10-26 22:02:19 +02:00
2023-10-28 03:47:23 +02:00
# Setup Data
setup_io_pins(OUT)
for j in range(8):
GPIO.output(IO[j], get_bit(byte, j))
sleep(t_write_DS)
# wait until minimum write pulse width
sleep(t_write_WP)
2023-10-26 19:00:17 +02:00
2023-10-27 02:41:58 +02:00
if debug:
clear_line()
echo(address, byte, mode="w", message="DEBUG: Press enter to continue")
input()
2023-10-26 19:00:17 +02:00
# End Write Pulse -> disable WEb
GPIO.output(WEb, 1)
2023-10-26 22:02:19 +02:00
2023-10-26 19:00:17 +02:00
# wait "Data Hold"
2023-10-28 03:47:23 +02:00
sleep(max(t_write_DH, t_write_WPH))
GPIO.cleanup(IO)
setup_io_pins(IN)
2023-10-26 19:00:17 +02:00
2023-10-28 03:47:23 +02:00
wait_write_finished()
GPIO.output(CEb, 1)
def write_page(address: int, page: list[int], verbose=True) -> int:
GPIO.output(OEb, 1)
# setup the address
set_address(address, bits=15)
2023-10-26 19:00:17 +02:00
GPIO.output(CEb, 0)
2023-10-28 03:47:23 +02:00
echo(address, 0, mode="p")
assert 64 - (address % 64) >= len(page), f"64 - (address % 64) ={64 - (address % 64)}!>={len(page)}=len(page)"
# wait setup time
sleep(max(t_write_CS, t_write_AS, t_write_OES))
# Setup Data
setup_io_pins(OUT)
for i in range(len(page)):
set_address(address + i, bits=6)
# Start the write pulse -> enable WEb
GPIO.output(WEb, 0)
for j in range(8):
GPIO.output(IO[j], get_bit(page[i], j))
sleep(max(t_write_DS, t_write_WP))
GPIO.output(WEb, 1)
sleep(t_write_WPH)
2023-10-26 19:00:17 +02:00
GPIO.cleanup(IO)
2023-10-26 22:02:19 +02:00
setup_io_pins(IN)
2023-10-26 19:00:17 +02:00
2023-10-28 03:47:23 +02:00
# wait_write_finished()
sleep(t_write_WC)
# print(f"Toggled {timeout} times")
GPIO.output(CEb, 1)
def wait_write_finished():
2023-10-26 19:00:17 +02:00
# check the toggle bit IO6, if it stops toggling the write is done
2023-10-28 03:47:23 +02:00
sleep(t_toggle_OEH)
2023-10-26 19:00:17 +02:00
timeout = 0
while timeout < 1e3:
GPIO.output(OEb, 0)
2023-10-28 03:47:23 +02:00
sleep(t_toggle_OE)
2023-10-26 19:00:17 +02:00
bit1 = GPIO.input(IO[6])
2023-10-28 03:47:23 +02:00
sleep(t_toggle_OEH)
2023-10-26 19:00:17 +02:00
GPIO.output(OEb, 1)
2023-10-28 03:47:23 +02:00
sleep(t_toggle_OEHP)
2023-10-26 19:00:17 +02:00
GPIO.output(OEb, 0)
2023-10-28 03:47:23 +02:00
sleep(t_toggle_OE)
2023-10-26 19:00:17 +02:00
bit2 = GPIO.input(IO[6])
2023-10-28 03:47:23 +02:00
sleep(t_toggle_OEH)
2023-10-26 19:00:17 +02:00
GPIO.output(OEb, 1)
2023-10-28 03:47:23 +02:00
sleep(t_toggle_OEHP)
2023-10-26 19:00:17 +02:00
if bit1 == bit2:
2023-10-28 03:47:23 +02:00
break
2023-10-26 19:00:17 +02:00
timeout += 1
2023-10-26 22:02:19 +02:00
2023-10-27 02:41:58 +02:00
def read_byte(address: int, verbose=True, debug=False) -> int:
2023-10-26 19:00:17 +02:00
GPIO.output(WEb, 1)
2023-10-26 22:02:19 +02:00
setup_io_pins(IN)
2023-10-26 19:00:17 +02:00
# set the address valid
2023-12-04 21:08:39 +01:00
set_address(address, bits=15)
2023-10-26 22:02:19 +02:00
2023-10-26 19:00:17 +02:00
# 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
2023-10-28 03:47:23 +02:00
sleep(max(t_read_ACC, t_read_CE, t_read_OE))
2023-10-27 02:41:58 +02:00
byte = 0
2023-10-26 19:00:17 +02:00
for j in range(8):
2023-10-27 02:41:58 +02:00
byte |= (GPIO.input(IO[j]) << j)
if debug:
clear_line()
echo(address, byte, mode="r", message="DEBUG: Press enter to continue")
input()
2023-10-26 22:02:19 +02:00
2023-10-26 19:00:17 +02:00
# high in OEb and CEb -> disable
GPIO.output(OEb, 1)
GPIO.output(CEb, 1)
2023-10-26 22:02:19 +02:00
2023-10-28 03:47:23 +02:00
sleep(t_read_DF)
2023-10-27 02:41:58 +02:00
if verbose: echo(address, byte, mode="r")
return byte
2023-10-26 19:00:17 +02:00
2023-10-27 02:41:58 +02:00
#
# FUNCTIONS
#
def read(from_ad=0, to_ad=255, delay=1e-3, ignore=[0xff], debug=False, compare:list[int]=[], verbose=True) -> list[int]:
2023-10-26 19:00:17 +02:00
"""
from_ad: start address from where to read
to_ad: end address to read to
delay: delay between readings in s
2023-10-27 02:41:58 +02:00
verbose: wether to print the reading
ignore: list of values which are not printed
2023-10-26 19:00:17 +02:00
"""
content = []
unequal = []
2023-10-27 02:41:58 +02:00
2023-10-28 03:47:23 +02:00
if compare:
assert len(compare) == to_ad - from_ad + 1, f"{len(compare)} vs = {to_ad - from_ad + 1} {to_ad} - {from_ad} + 1"
2023-10-26 19:00:17 +02:00
for i in range(from_ad, to_ad + 1):
2023-10-27 02:41:58 +02:00
byte = read_byte(i, verbose=verbose, debug=debug)
2023-10-26 19:00:17 +02:00
content.append(byte)
2023-10-26 22:02:19 +02:00
2023-10-27 02:41:58 +02:00
if compare:
2023-10-28 03:47:23 +02:00
if not compare[i-from_ad] == content[i-from_ad]:
2023-10-26 19:00:17 +02:00
unequal.append(i)
2023-10-27 02:41:58 +02:00
echo(i, byte, "vs {" + format_number(compare[i-from_ad], hex_digits=2, bin_digits=8, dec_digits=3) + "} in file", mode="c", end="\n")
2023-10-26 19:00:17 +02:00
# wait artifical delay
sleep(delay)
2023-10-27 02:41:58 +02:00
clear_line()
2023-10-26 19:00:17 +02:00
if compare:
return unequal
return content
2023-10-26 22:02:19 +02:00
2023-10-28 03:47:23 +02:00
def write(content: list[int], from_ad=0, delay=0, debug=False, only_diff=True, verbose=True, check_written=True, use_page_write=False):
2023-10-26 19:00:17 +02:00
"""
2023-10-26 22:02:19 +02:00
Write a list if bytes to the eeprom.
2023-10-26 19:00:17 +02:00
WEb controlled
"""
2023-10-27 02:41:58 +02:00
to_ad = from_ad+len(content)-1
current_content = []
if only_diff:
print(f"Reading EEPROM and only writing deviating values")
current_content = read(from_ad, to_ad=to_ad, delay=delay, debug=debug, verbose=True)
2023-10-28 03:47:23 +02:00
assert len(content) == len(current_content)
2023-10-26 19:00:17 +02:00
failed = []
2023-10-27 02:41:58 +02:00
print(f"Writing to EEPROM: {len(content)} bytes from address {format(from_ad, '04x')} to {format(to_ad, '04x')}.")
2023-10-26 19:00:17 +02:00
2023-10-28 03:47:23 +02:00
n_written = 0
n_written_pages = 0
if use_page_write:
# assert from_ad == 0, "from_ad != 0 not supported yet"
# assert (to_ad+1) % 64 == 0, f"to_ad-1 % 64 != 0 not supported yet. to_ad={to_ad}, to_ad+1% 64 = {(to_ad+1) % 64}"
for i in range(from_ad, to_ad, 64):
# print(i, i+64, i+64 - (i%64))
page_end = i + 64 - (i % 64)
page = content[i:page_end]
# print(page, current_content[i:page_end])
if only_diff and page == current_content[i:page_end]:
continue
write_page(i, page)
n_written_pages += 1
n_written += len(page)
sleep(delay)
else:
for i in range(from_ad, to_ad + 1):
if only_diff and (only_diff and content[i-from_ad] != current_content[i-from_ad]):
continue
write_byte(content[i-from_ad], i, verbose=True, debug=debug)
n_written += 1
2023-10-27 02:41:58 +02:00
# wait artifical delay
sleep(delay)
2023-10-28 03:47:23 +02:00
2023-10-27 02:41:58 +02:00
clear_line()
2023-10-28 03:47:23 +02:00
print(f"Written " + (f"{n_written_pages} pages / " if use_page_write else "") + f"{n_written} bytes")
2023-10-26 19:00:17 +02:00
if check_written:
print("Comparing EEPROM to file...")
2023-10-28 03:47:23 +02:00
failed = read(from_ad=from_ad, to_ad=to_ad, delay=delay, debug=debug, verbose=True, compare=content)
retries = 2
2023-10-27 02:41:58 +02:00
while len(failed) > 0 and retries > 0:
print(f"Found {len(failed)} bytes deviating from the file - retrying: {retries} times")
2023-10-26 19:00:17 +02:00
for ad in failed:
2023-10-27 02:41:58 +02:00
write_byte(content[ad-from_ad], ad, verbose=verbose)
failed = read(from_ad=from_ad, to_ad=to_ad, delay=delay, debug=debug, verbose=False, compare=content)
retries -= 1
clear_line()
2023-10-26 19:00:17 +02:00
print("Comparing complete")
2023-10-27 02:41:58 +02:00
if len(failed) > 0:
print(f"Errors occured: {len(failed)} values could not be written:")
print("[", end="")
for ad in failed:
print(format(ad, '04x'), end=",")
print("]")
2023-10-26 19:00:17 +02:00
return
2023-10-26 22:02:19 +02:00
2023-10-27 02:41:58 +02:00
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
2023-10-26 19:00:17 +02:00
2023-10-26 22:02:19 +02:00
def main():
2023-10-27 02:41:58 +02:00
global REP_CHAR, PRINT_BIN, PRINT_DEC, PRINT_HEX
2023-10-26 22:02:19 +02:00
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")
2023-10-28 03:47:23 +02:00
parser.add_argument("--page", "-p", dest="use_page_write", action="store_true", default=False, help="use page write mode")
parser.add_argument("--no-validate", dest="validate_write", action="store_false", default=True, help="dont validate write operations")
2023-12-04 21:08:39 +01:00
parser.add_argument("--full", dest="only_diff", action="store_false", default=True, help="write all bytes, not just ones that are mismatched")
2023-10-26 22:02:19 +02:00
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")
2023-10-27 02:41:58 +02:00
parser.add_argument("--to", metavar="end-address", dest="to_ad", action="store", default=0x7fff, type=convert_to_int, help="end at address y (inclusive)")
parser.add_argument("--debug", dest="debug", action="store_true", default=False, help="stop the program after setting up the address")
parser.add_argument("--delay", dest="delay", metavar="delay", action="store", type=float, default=0.0, help="extra delay between cycles")
2023-10-26 22:02:19 +02:00
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]")
2023-10-27 02:41:58 +02:00
parser.add_argument("--verbose", "-v", dest="verbose", action="store_true", default=False, help="increase verbosity")
2023-10-26 22:02:19 +02:00
parser.add_argument("--print-pins", dest="print_pins", action="store_true", default=False, help="print the pin layout and exit")
2023-10-27 02:41:58 +02:00
parser.add_argument("--no-hex", dest="no_hex", action="store_true", default=False, help="Dont print values hex")
parser.add_argument("--bin", dest="bin", action="store_true", default=False, help="Print values in binary")
2023-10-26 22:02:19 +02:00
args = parser.parse_args()
if (args.write or args.compare) and not args.file:
parser.error('--file is required with --write and --compare')
2023-10-27 02:41:58 +02:00
if not args.verbose: REP_CHAR = '\r'
if args.no_hex: PRINT_HEX = False
if args.bin: PRINT_BIN = True
2023-10-28 03:47:23 +02:00
if args.file:
file = read_binary_file(args.file, from_ad=args.from_ad, to_ad=args.to_ad)
if args.to_ad != args.from_ad + len(file) - 1:
args.to_ad = args.from_ad + len(file) - 1
else:
file = None
2023-10-26 22:02:19 +02:00
if args.print_pins:
print_pins()
exit(0)
if args.erase:
2023-10-28 03:47:23 +02:00
erase(from_ad=args.from_ad, to_ad=args.to_ad, delay=args.delay, debug=args.debug, verbose=True)
2023-10-26 22:02:19 +02:00
if args.write:
2023-12-04 21:08:39 +01:00
write(file, from_ad=args.from_ad, delay=args.delay, debug=args.debug, verbose=True, check_written=args.validate_write, use_page_write=args.use_page_write, only_diff=args.only_diff)
2023-10-26 22:02:19 +02:00
if args.read:
2023-10-28 03:47:23 +02:00
read(from_ad=args.from_ad, to_ad=args.to_ad, delay=args.delay, debug=args.debug, verbose=True, ignore=args.ignore)
2023-10-26 22:02:19 +02:00
if args.compare:
2023-10-28 03:47:23 +02:00
unequal = read(from_ad=args.from_ad, to_ad=args.to_ad, delay=args.delay, debug=args.debug, verbose=args.verbose, ignore=args.ignore, compare=file)
2023-10-27 02:41:58 +02:00
print(f"Found {len(unequal)} deviating values")
2023-10-26 22:02:19 +02:00
if __name__ == '__main__':
atexit.register(cleanup)
2023-10-27 02:41:58 +02:00
setup_pins()
2023-10-26 22:02:19 +02:00
main()
2023-10-27 02:41:58 +02:00
# PRINT_BIN = True
# for i in range(15):
# address = (1 << i)
# PRINT_BIN = True
# set_address(address=address)
# input(f"DEBUG: {format_number(address, hex_digits=4, bin_digits=15)}")