123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305 |
- #!/usr/bin/env python3
- """
- Interface to program SPI Flash using FPGC as programmer
- """
- import serial
- from time import sleep
- import sys
- import fileinput
- import os
- from tqdm import tqdm
- import argparse
- BLOCKSIZE = 32768
- PAGESIZE = 256
- parser = argparse.ArgumentParser(description='Interface to FPGC SPI Flash Programmer')
- parser.add_argument('-d', dest='device', default='/dev/ttyUSB1',
- help='serial port to communicate with')
- parser.add_argument('-i', dest='input', default='code.bin',
- help='file to read from')
- parser.add_argument('-o', dest='output', default='dump.bin',
- help='file to write to')
- parser.add_argument('-l', type=int, dest='length', default=4096,
- help='length to read in bytes')
- parser.add_argument('-a', type=int, dest='address', default=0,
- help='address offset (TBI)')
- parser.add_argument('--rate', type=int, dest='baud_rate', default=1000000,
- help='baud-rate of serial connection')
- parser.add_argument('-v', dest='verify', default=None,
- help='filename for verification (enables verification)')
- parser.add_argument('command', choices=('read', 'write', 'erase', 'id', 'status'),
- help='command to execute')
- args = parser.parse_args()
- port = serial.Serial(args.device, baudrate=args.baud_rate, timeout=None)
- """
- SENDING PROGRAMMER BINARY TO FPGC
- """
- sleep(0.3) # give the FPGC time to reset, even though it also works without this delay
- # parse byte file
- ba = bytearray()
- # Use flasher_accurate.bin or flasher_fast.bin
- # and change fillBuffer() accordingly
- with open("flasher_fast.bin", "rb") as f:
- bytes_read = f.read()
- for b in bytes_read:
- ba.append(b)
- # combine each 4 bytes into a word
- n = 4
- wordList = [ba[i * n:(i + 1) * n] for i in range((len(ba) + n - 1) // n )]
- # size of program is in address 2
- fileSize = bytes(wordList[2])
- #print(int.from_bytes(fileSize, "big"), flush=True)
- print("Writing flash programmer to FPGC")
- # write filesize
- port.write(fileSize)
- # read four bytes
- rcv = port.read(4)
- # to verify if communication works
- #print(rcv, flush=True)
- # send all words
- doneSending = False
- wordCounter = 0
- while not doneSending:
- port.write(bytes(wordList[wordCounter]))
- wordCounter = wordCounter + 1
- if (wordCounter == int.from_bytes(fileSize, "big")):
- doneSending = True
- port.read(1) # should return 'd', though I'm not checking on it
- print("Done programming FPGC", flush=True)
- """
- INTERACTING WITH PROGRAMMER
- """
- # Send single byte, wait a bit and assume successful receive
- # Speed depends a lot on USB configuration (like hubs),
- # so change the delay between ~0.001 and 0.00005
- def sendSingleByte(b):
- port.write(b)
- sleep(0.0004) # give FPGC time to process byte
- # Send single byte, wait for ACk from FPGC
- def sendSingleByteWaitForAck(b):
- port.write(b)
- port.read(1) # should return 'a', to indicate successful receive
- def eraseBlock(addr):
- port.read(1) # should return 'r', to indicate ready to accept command
- sendSingleByte( 'e'.encode('utf-8') )
- size = 0
- blist = size.to_bytes(3, byteorder="big")
- for b in blist:
- sendSingleByte(b.to_bytes(1, byteorder="big"))
- blist = addr.to_bytes(3, byteorder="big")
- for b in blist:
- sendSingleByte(b.to_bytes(1, byteorder="big"))
- sendSingleByte( '\n'.encode('utf-8') )
- #print("block erased")
- def fillBuffer(page):
- port.read(1) # should return 'r', to indicate ready to accept command
- sendSingleByte( 'b'.encode('utf-8') )
- size = 0
- addr = 0
- blist = size.to_bytes(3, byteorder="big")
- for b in blist:
- sendSingleByte(b.to_bytes(1, byteorder="big"))
- blist = addr.to_bytes(3, byteorder="big")
- for b in blist:
- sendSingleByte(b.to_bytes(1, byteorder="big"))
- sendSingleByte( '\n'.encode('utf-8') )
- # command is sent, now wait for 'b' to write 256 bytes
- port.read(1)
- for i in range(256):
- # Slow, but accurate (needs flasher_accurate.bin):
- #sendSingleByteWaitForAck(page[i].to_bytes(1, byteorder="big"))
-
- # Fast, but needs finetuning on host device (needs flasher_fast.bin)
- sendSingleByte(page[i].to_bytes(1, byteorder="big"))
- #print("page sent")
- def writeBuffer(addr):
- port.read(1) # should return 'r', to indicate ready to accept command
- sendSingleByte( 'w'.encode('utf-8') )
- size = 0
- blist = size.to_bytes(3, byteorder="big")
- for b in blist:
- sendSingleByte(b.to_bytes(1, byteorder="big"))
- blist = addr.to_bytes(3, byteorder="big")
- for b in blist:
- sendSingleByte(b.to_bytes(1, byteorder="big"))
- sendSingleByte( '\n'.encode('utf-8') )
- #print("page written")
- def writePage(page, addr):
- fillBuffer(page) # fill buffer with page
- writeBuffer(addr) # tell FPGC to write the buffer to addr
-
- def readFlash(size, addr, filename):
- port.read(1) # should return 'r', to indicate ready to accept command
- sendSingleByte( 'r'.encode('utf-8') )
- blist = size.to_bytes(3, byteorder="big")
- for b in blist:
- sendSingleByte(b.to_bytes(1, byteorder="big"))
- blist = addr.to_bytes(3, byteorder="big")
- for b in blist:
- sendSingleByte(b.to_bytes(1, byteorder="big"))
- sendSingleByte( '\n'.encode('utf-8') )
- with open(filename, 'wb') as f:
- for i in tqdm(range(size)):
- rcv = port.read(1)
- f.write(rcv)
- #print("read done")
- # Writes inFile to addr.
- # If verify, then read and dump to outFile and diff on inFile and outFile
- def writeFlash(inFile, addr = 0, verify = False, outFile = "verify.bin"):
- # get size of inFile
- size = os.stat(inFile).st_size
- print("Going to write " + str(size) + " bytes")
- # erase before programming
- blocksToErase = -(-size // BLOCKSIZE) # number of 32KiB blocks to erase
- blocksErased = 0
- for i in range(blocksToErase):
- eraseBlock(i*BLOCKSIZE)
- blocksErased+=1
- print("Erased " + str(blocksErased) + " blocks")
- # get bytes to write
- ba = bytearray()
- # read binary file to byte array
- with open(inFile, "rb") as f:
- bytes_read = f.read()
- for b in bytes_read:
- ba.append(b)
- print("Writing pages")
- # loop in chunks of 256 bytes
- chunk_size= PAGESIZE
- for i in tqdm(range(0, len(ba), chunk_size)):
- chunk = ba[i:i+chunk_size]
- # write chunk
- writePage(chunk, addr)
- addr = addr + PAGESIZE
- print()
- # verify
- if verify:
- print("Reading data to verify")
- readFlash(size, 0, outFile)
- if os.system("diff " + inFile + " " + outFile):
- print("ERROR: WRITE FAILED!")
- else:
- print("Write successful")
-
- print()
- print("---FPGC FLASH PROGRAMMER---")
- if args.command == "read":
- readFlash(args.length, args.address, args.output)
- elif args.command == "write":
- enableVerify = False
- if args.verify:
- enableVerify = True
- writeFlash(args.input, args.address, enableVerify, args.verify)
- else:
- print("Command not fully implemented yet")
- # notify done
- port.read(1) # should return 'r', to indicate ready to accept command
- for x in range(8):
- sendSingleByte( 'd'.encode('utf-8') )
|