265 lines
7.6 KiB
Python
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)
|