flashprog/flashprog.py

265 lines
7.6 KiB
Python

# copyright is bullshit bgdc
# flashprog - circuitpython parallel flash programmer for Teensy 4.1
import digitalio
import board
import binascii
import time
# A0 ... A18
PINS_A = [
board.D11, board.D12, board.D13, board.D14,
board.D15, board.D18, board.D17, board.D16,
board.D19, board.D20, board.D21, board.D22,
board.D23, board.D24, board.D25, board.D26,
board.D27, board.D28, board.D29
]
# D0 ... D7
PINS_D = [
board.D0, board.D1, board.D2, board.D3,
board.D4, board.D5, board.D6, board.D7
]
PIN_nWE = board.D8
PIN_nOE = board.D9
PIN_nCE = board.D10
SEC_MASK = 0xFFF
class FlashProg:
def __init__(self):
assert(len(PINS_D) == 8)
self.a = [digitalio.DigitalInOut(i) for i in PINS_A]
self.d = [digitalio.DigitalInOut(i) for i in PINS_D]
self.nwe = digitalio.DigitalInOut(PIN_nWE)
self.noe = digitalio.DigitalInOut(PIN_nOE)
self.nce = digitalio.DigitalInOut(PIN_nCE)
for i in self.a:
i.switch_to_input(pull=digitalio.Pull.DOWN)
for i in self.d:
i.switch_to_input(pull=digitalio.Pull.DOWN)
self.nwe.switch_to_input(pull=digitalio.Pull.UP)
self.noe.switch_to_input(pull=digitalio.Pull.UP)
self.nce.switch_to_output(1)
def read(self, addr): # read an address, returning the value
self.nwe.switch_to_output(1)
for i in self.a:
i.switch_to_output(bool(addr & 1))
addr >>= 1
self.nce.value = 0
self.noe.switch_to_output(0)
data = 0
bit = 1
for i in self.d:
if i.value:
data |= bit
bit <<= 1
self.noe.value = 1
self.noe.switch_to_input(pull=digitalio.Pull.UP)
for i in self.a:
i.switch_to_input(pull=digitalio.Pull.DOWN)
self.nce.value = 1
self.nwe.switch_to_input(pull=digitalio.Pull.UP)
return data
# perform a simple write; does not implement the flash protocol
# can optionally perform DQ6 Polling after write
def wr(self, addr, data, poll=False):
self.nwe.switch_to_output(1)
self.noe.switch_to_output(1)
for i in self.a:
i.switch_to_output(bool(addr & 1))
addr >>= 1
for i in self.d:
i.switch_to_output(bool(data & 1))
data >>= 1
self.nce.value = 0
self.nwe.value = 0
self.nwe.value = 1
self.noe.switch_to_input(pull=digitalio.Pull.UP)
self.nce.value = 1
for i in self.a:
i.switch_to_input(pull=digitalio.Pull.DOWN)
for i in self.d:
i.switch_to_input(pull=digitalio.Pull.DOWN)
self.nwe.switch_to_input(pull=digitalio.Pull.UP)
if poll:
d = self.read(0)
while True:
d2 = self.read(0)
if d2 == d:
break
d = d2
# perform a byte program operation
def prog(self, addr, data):
self.wr(0x5555, 0xAA)
self.wr(0x2AAA, 0x55)
self.wr(0x5555, 0xA0)
self.wr(addr, data, poll=True)
# perform a sector erase operation
def erase(self, addr):
addr &= ~SEC_MASK
self.wr(0x5555, 0xAA)
self.wr(0x2AAA, 0x55)
self.wr(0x5555, 0x80)
self.wr(0x5555, 0xAA)
self.wr(0x2AAA, 0x55)
self.wr(addr, 0x30, poll=True)
# perform a chip erase operation
def erase_chip(self):
self.wr(0x5555, 0xAA)
self.wr(0x2AAA, 0x55)
self.wr(0x5555, 0x80)
self.wr(0x5555, 0xAA)
self.wr(0x2AAA, 0x55)
self.wr(0x5555, 0x10, poll=True)
# enter software id mode
def enter_swid(self):
self.wr(0x5555, 0xAA)
self.wr(0x2AAA, 0x55)
self.wr(0x5555, 0x90)
# exit software id mode
def exit_swid(self):
self.wr(0x5555, 0xAA)
self.wr(0x2AAA, 0x55)
self.wr(0x5555, 0xF0)
# Get the chip IDs. Returns (manuf, dev).
def get_id(self):
self.enter_swid()
manuf = self.read(0x0000)
dev = self.read(0x0001)
self.exit_swid()
return manuf, dev
# Parse and program an s-record line. If the address aligns with a sector
# boundary, erases the sector
def srec(self, s):
assert s[0] == "S"
if s[1] == "1":
addrlen = 2
elif s[1] == "2":
addrlen = 3
elif s[1] == "3":
addrlen = 4
else:
print("Skipping S" + s[1] + " record")
return
raw = binascii.unhexlify(s[2:])
count = raw[0]
assert len(raw) == count + 1
addr_raw = raw[1:1+addrlen]
cksum = raw[-1]
data = raw[1+addrlen : -1]
computed_cksum = ~(sum(addr_raw) + sum(data) + count) & 0xFF
assert cksum == computed_cksum
addr = 0
for i in addr_raw:
addr <<= 8
addr |= i
if (addr & SEC_MASK) == 0:
print(f"Erasing sector at {addr:06X}")
self.erase(addr)
for i in data:
self.prog(addr, i)
addr += 1
# Start a loop accepting s-record lines or commands.
def run():
fp = FlashProg()
print("flashprog - enter help for usage")
while True:
cmd = input("> ").strip()
if not cmd:
continue
try:
if cmd[0] == "S":
fp.srec(cmd)
elif cmd == "erase":
fp.erase_chip()
elif cmd.startswith("erase"):
parts = cmd.split()
if len(parts) == 2:
addr = int(parts[1], 16)
fp.erase(addr)
elif len(parts) == 3:
addr_from = int(parts[1], 16)
addr_to = int(parts[2], 16)
while addr_from <= addr_to:
fp.erase(addr_from)
addr_from += SEC_MASK + 1
elif cmd.startswith("read"):
parts = cmd.split()
if len(parts) == 2:
addr = int(parts[1], 16)
data = fp.read(addr)
print(f"{addr:06X} = {data:02X}")
elif len(parts) == 3:
addr_from = int(parts[1], 16)
addr_to = int(parts[2], 16)
data = bytearray(32)
i = 0
while addr_from <= addr_to:
if i == 0:
print(f"{addr_from:06X} ", end='')
data[i] = fp.read(addr_from)
addr_from += 1
i += 1
if i == 32:
print(binascii.hexlify(data).decode('ascii'))
i = 0
if i < 32:
print(binascii.hexlify(data[:i]).decode('ascii'))
elif cmd == "info":
manuf, dev = fp.get_id()
print(f"Manuf: {manuf:02X}")
print(f"Dev: {dev:02X}")
elif cmd == "help":
print("S record lines - write them to the chip")
print("read FROM [TO] - read data at FROM to TO")
print("erase - erase chip")
print("erase FROM [TO] - erase sectors FROM to TO")
print("info - print chip info")
print("help - this text")
print("exit")
elif cmd == "exit":
break
else:
print("unknown command")
except Exception as e:
print(e)