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 ]
#
# 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-27 02:41:58 +02:00
2 , 3 , 17 , 4 , # A8 - A11
2023-10-26 22:02:19 +02:00
23 , 18 , 15 # A12 - A14
]
2023-10-26 19:00:17 +02:00
2023-10-26 22:02:19 +02:00
# IO = gpio_r[4:12] # 8 io pins
# IO.reverse()
IO = [ 24 , 25 , 8 , 7 , 12 , 16 , 20 , 21 ]
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:
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
2023-10-27 02:41:58 +02:00
t_WPH = 50 * 1e-4 # Write Pulse High !!!2*e5 longer than in Datasheet, since shorter high caused Problems with my Chip!!!
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-27 02:41:58 +02:00
def read_binary_file ( filepath , from_ad = 0 ) - > list [ int ] :
with open ( filepath , " rb " ) as file :
bindata = [ ]
for byte in file . read ( ) :
bindata . append ( byte )
return bindata [ from_ad : ]
#
# 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 )
setup_io_pins ( OUT )
2023-10-26 19:00:17 +02:00
# Setup Data
for j in range ( 8 ) :
2023-10-27 02:41:58 +02:00
GPIO . output ( IO [ j ] , get_bit ( byte , j ) )
2023-10-26 19:00:17 +02:00
# wait "Address" Setup Time
sleep ( t_AS )
# wait "Data Setup Time"
sleep ( t_DS )
2023-10-26 22:02:19 +02:00
2023-10-26 19:00:17 +02:00
GPIO . output ( CEb , 0 )
# Start the write pulse -> enable WEb
GPIO . output ( WEb , 0 )
2023-10-26 22:02:19 +02:00
2023-10-26 19:00:17 +02:00
# wait until minimum write pulse width is reached. in theory, should be t_WP-t_DS but this caused problems
sleep ( t_WP )
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 )
GPIO . output ( CEb , 1 )
2023-10-26 22:02:19 +02:00
2023-10-26 19:00:17 +02:00
# wait "Data Hold"
sleep ( t_DH )
GPIO . output ( CEb , 0 )
GPIO . cleanup ( IO )
2023-10-26 22:02:19 +02:00
setup_io_pins ( IN )
2023-10-26 19:00:17 +02:00
# 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 )
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
ad_bin = 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
sleep ( t_ACC )
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-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-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 :
if not compare [ i - from_ad ] == byte :
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-27 02:41:58 +02:00
def write ( content : list [ int ] , from_ad = 0 , delay = 0 , debug = False , only_diff = True , verbose = True , check_written = True ) :
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 )
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
for i in range ( len ( content ) ) :
2023-10-27 02:41:58 +02:00
if not only_diff or ( only_diff and content [ i ] != current_content [ i ] ) :
write_byte ( content [ i ] , from_ad + i , verbose = True , debug = debug )
# wait artifical delay
sleep ( delay )
clear_line ( )
2023-10-26 19:00:17 +02:00
print ( " Write to EEPROM - Done! " )
if check_written :
print ( " Comparing EEPROM to file... " )
2023-10-27 02:41:58 +02:00
failed = read ( from_ad = from_ad , to_ad = to_ad , delay = delay , debug = debug , verbose = False , compare = content )
retries = 1
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 \n 2023 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 " )
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-26 22:02:19 +02:00
if args . print_pins :
print_pins ( )
exit ( 0 )
if args . erase :
2023-10-27 02:41:58 +02:00
erase ( from_ad = args . from_ad , to_ad = args . to_ad , delay = args . delay , debug = args . debug , verbose = args . verbose )
2023-10-26 22:02:19 +02:00
if args . write :
2023-10-27 02:41:58 +02:00
write ( read_binary_file ( args . file , from_ad = args . from_ad ) , from_ad = args . from_ad , delay = args . delay , debug = args . debug , verbose = args . verbose )
2023-10-26 22:02:19 +02:00
if args . read :
2023-10-27 02:41:58 +02:00
read ( from_ad = args . from_ad , to_ad = args . to_ad , delay = args . delay , debug = args . debug , verbose = args . verbose , ignore = args . ignore )
2023-10-26 22:02:19 +02:00
if args . compare :
2023-10-27 02:41:58 +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 = read_binary_file ( args . file ) )
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)}")