Initial commit
commit
b5626af169
|
@ -0,0 +1,265 @@
|
|||
# copyright is bullshit bgdc
|
||||
|
||||
# flashprog - circuitpython parallel flash programmer for Teensy 4.1
|
||||
|
||||
import digitalio
|
||||
import board
|
||||
|
||||
import binascii
|
||||
|
||||
# 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)
|
||||
self.nce.value = 0
|
||||
for i in self.a:
|
||||
i.switch_to_output(bool(addr & 1))
|
||||
addr >>= 1
|
||||
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.nce.value = 0
|
||||
for i in self.a:
|
||||
i.switch_to_output(bool(addr & 1))
|
||||
addr >>= 1
|
||||
self.noe.switch_to_output(1)
|
||||
|
||||
for i in self.d:
|
||||
i.switch_to_output(bool(data & 1))
|
||||
data >>= 1
|
||||
|
||||
self.nwe.value = 0
|
||||
self.nwe.value = 1
|
||||
|
||||
if poll:
|
||||
self.noe.value = 0
|
||||
dq6 = self.d[6].value
|
||||
self.noe.value = 1
|
||||
while True:
|
||||
self.noe.value = 0
|
||||
new_dq6 = self.d[6].value
|
||||
self.noe.value = 1
|
||||
if new_dq6 == dq6:
|
||||
break
|
||||
dq6 = new_dq6
|
||||
|
||||
self.noe.switch_to_input(pull=digitalio.Pull.UP)
|
||||
|
||||
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.nce.value = 1
|
||||
self.nwe.switch_to_input(pull=digitalio.Pull.UP)
|
||||
|
||||
# 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
|
||||
except Exception as e:
|
||||
print(e)
|
Loading…
Reference in New Issue