diff --git a/eeprom.py b/eeprom.py index 452718a..ed6b07d 100755 --- a/eeprom.py +++ b/eeprom.py @@ -19,7 +19,7 @@ gpio_r = [14, 15, 18, 23, 24, 25, 8, 7, 12, 16, 20, 21] 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 + 2, 3, 17, 4, # A8 - A11 23, 18, 15 # A12 - A14 ] @@ -45,27 +45,18 @@ 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!!! +t_WPH = 50 * 1e-4 # Write Pulse High !!!2*e5 longer than in Datasheet, since shorter high caused Problems with my Chip!!! + +PRINT_HEX = True +PRINT_BIN = False +PRINT_DEC = False +REP_CHAR = '\n' -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() - - +# +# UTILITY +# def print_pins(): for i in range(len(A)): print(f" A{i:02} - {A[i]}") @@ -76,25 +67,29 @@ def print_pins(): 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 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)] -def get_bits(i: int): +def echo(address: int, value: int, message: str="", mode: str="-", end=None): """ - return how many bits are needed to express the number in binary + print a message for a given address and value """ - return len(bin(i)) - 2 # -2 for the "0x" + 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') def convert_to_int(v): @@ -108,35 +103,71 @@ def convert_to_int(v): 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): +def get_n_bits(i: int): """ - Write all 1 to the EEPROM - WEb controlled + return how many bits are needed to express the number in binary """ - data = [0xff for i in range(from_ad, to_ad)] - write(data, from_ad=from_ad, **keys) - print("Erased EEPROM - Done!") - return + return len(bin(i)) - 2 # -2 for the "0x" -def write_byte(byte, address, verbose=True): +def get_bit(value: int, n: int): + """ + get the n-th bit (0-based) + """ + return value & (1 << n) + + +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") + GPIO.output(OEb, 1) # setup the address - ad_bin = set_address(address, bits=15) - setup_io_pins(OUT) + 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) + GPIO.output(IO[j], get_bit(byte, j)) # wait "Address" Setup Time sleep(t_AS) # wait "Data Setup Time" @@ -149,6 +180,12 @@ def write_byte(byte, address, verbose=True): # wait until minimum write pulse width is reached. in theory, should be t_WP-t_DS but this caused problems sleep(t_WP) + if debug: + clear_line() + echo(address, byte, mode="w", message="DEBUG: Press enter to continue") + input() + + # End Write Pulse -> disable WEb GPIO.output(WEb, 1) GPIO.output(CEb, 1) @@ -177,11 +214,9 @@ def write_byte(byte, address, verbose=True): 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): +def read_byte(address: int, verbose=True, debug=False) -> int: GPIO.output(WEb, 1) setup_io_pins(IN) # set the address valid @@ -194,85 +229,111 @@ def read_byte(address): # wait the "Address to Output Delay" until the output is valid sleep(t_ACC) - byte = "" + + byte = 0 for j in range(8): - if GPIO.input(IO[7-j]) == 1: - byte += "1" - else: - byte += "0" + byte |= (GPIO.input(IO[j]) << j) + + if debug: + clear_line() + echo(address, byte, mode="r", message="DEBUG: Press enter to continue") + input() # high in OEb and CEb -> disable GPIO.output(OEb, 1) GPIO.output(CEb, 1) - return int(byte, 2) + + if verbose: echo(address, byte, mode="r") + + return byte -def read(from_ad=0, to_ad=255, delay=1e-3, ignore=[0xff], verbose=True, single_step=False, compare=None): +# +# FUNCTIONS +# +def read(from_ad=0, to_ad=255, delay=1e-3, ignore=[0xff], debug=False, compare:list[int]=[], verbose=True) -> list[int]: """ 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 + 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) + byte = read_byte(i, verbose=verbose, debug=debug) 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: + if compare: + if not compare[i-from_ad] == 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')}") + 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") # wait artifical delay sleep(delay) - if single_step: - input("Press Return to read the next byte") + clear_line() if compare: return unequal return content -def write(content: list, from_ad=0, delay=0, single_step=False, verbose=True, check_written=True): +def write(content: list[int], from_ad=0, delay=0, debug=False, only_diff=True, verbose=True, check_written=True): """ Write a list if bytes to the eeprom. WEb controlled """ - or_content = content.copy() - content = get_8_bit(content) + 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) failed = [] - print(f"Writing to EEPROM: {len(content)} bytes from address {hex(from_ad)}.") + print(f"Writing to EEPROM: {len(content)} bytes from address {format(from_ad, '04x')} to {format(to_ad, '04x')}.") 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") + 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() 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: + 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") 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) + 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() print("Comparing complete") + 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("]") 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 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 main(): + global REP_CHAR, PRINT_BIN, PRINT_DEC, PRINT_HEX 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", @@ -284,32 +345,46 @@ def main(): 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("--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") 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("--verbose", "-v", 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") + 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") args = parser.parse_args() if (args.write or args.compare) and not args.file: parser.error('--file is required with --write and --compare') + if not args.verbose: REP_CHAR = '\r' + if args.no_hex: PRINT_HEX = False + if args.bin: PRINT_BIN = True + 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) + erase(from_ad=args.from_ad, to_ad=args.to_ad, delay=args.delay, debug=args.debug, 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) + 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) 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) + read(from_ad=args.from_ad, to_ad=args.to_ad, delay=args.delay, debug=args.debug, 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)) + 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") if __name__ == '__main__': atexit.register(cleanup) - setup_address_control_pins() + setup_pins() main() + +# 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)}")