flash.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. #!/usr/bin/env python3
  2. """
  3. Interface to program SPI Flash using FPGC as programmer
  4. """
  5. import serial
  6. from time import sleep
  7. import sys
  8. import fileinput
  9. import os
  10. from tqdm import tqdm
  11. import argparse
  12. BLOCKSIZE = 32768
  13. PAGESIZE = 256
  14. parser = argparse.ArgumentParser(description='Interface to FPGC SPI Flash Programmer')
  15. parser.add_argument('-d', dest='device', default='/dev/ttyUSB0',
  16. help='serial port to communicate with')
  17. parser.add_argument('-i', dest='input', default='code.bin',
  18. help='file to read from')
  19. parser.add_argument('-o', dest='output', default='dump.bin',
  20. help='file to write to')
  21. parser.add_argument('-l', type=int, dest='length', default=4096,
  22. help='length to read in bytes')
  23. parser.add_argument('-a', type=int, dest='address', default=0,
  24. help='address offset (TBI)')
  25. parser.add_argument('--rate', type=int, dest='baud_rate', default=1000000,
  26. help='baud-rate of serial connection')
  27. parser.add_argument('-v', dest='verify', default=None,
  28. help='filename for verification (enables verification)')
  29. parser.add_argument('command', choices=('read', 'write', 'erase', 'id', 'status'),
  30. help='command to execute')
  31. args = parser.parse_args()
  32. port = serial.Serial(args.device, baudrate=args.baud_rate, timeout=None)
  33. """
  34. SENDING PROGRAMMER BINARY TO FPGC
  35. """
  36. sleep(0.3) # give the FPGC time to reset, even though it also works without this delay
  37. # parse byte file
  38. ba = bytearray()
  39. # Use flasher_accurate.bin or flasher_fast.bin
  40. # and change fillBuffer() accordingly
  41. with open("flasher_fast.bin", "rb") as f:
  42. bytes_read = f.read()
  43. for b in bytes_read:
  44. ba.append(b)
  45. # combine each 4 bytes into a word
  46. n = 4
  47. wordList = [ba[i * n:(i + 1) * n] for i in range((len(ba) + n - 1) // n )]
  48. # size of program is in address 2
  49. fileSize = bytes(wordList[2])
  50. #print(int.from_bytes(fileSize, "big"), flush=True)
  51. print("Writing flash programmer to FPGC")
  52. # write filesize
  53. port.write(fileSize)
  54. # read four bytes
  55. rcv = port.read(4)
  56. # to verify if communication works
  57. #print(rcv, flush=True)
  58. # send all words
  59. doneSending = False
  60. wordCounter = 0
  61. while not doneSending:
  62. port.write(bytes(wordList[wordCounter]))
  63. wordCounter = wordCounter + 1
  64. if (wordCounter == int.from_bytes(fileSize, "big")):
  65. doneSending = True
  66. port.read(1) # should return 'd', though I'm not checking on it
  67. print("Done programming FPGC", flush=True)
  68. """
  69. INTERACTING WITH PROGRAMMER
  70. """
  71. # Send single byte, wait a bit and assume successful receive
  72. # Speed depends a lot on USB configuration (like hubs),
  73. # so change the delay between ~0.001 and 0.00005
  74. def sendSingleByte(b):
  75. port.write(b)
  76. sleep(0.0004) # give FPGC time to process byte
  77. # Send single byte, wait for ACk from FPGC
  78. def sendSingleByteWaitForAck(b):
  79. port.write(b)
  80. port.read(1) # should return 'a', to indicate successful receive
  81. def eraseBlock(addr):
  82. port.read(1) # should return 'r', to indicate ready to accept command
  83. sendSingleByte( 'e'.encode('utf-8') )
  84. size = 0
  85. blist = size.to_bytes(3, byteorder="big")
  86. for b in blist:
  87. sendSingleByte(b.to_bytes(1, byteorder="big"))
  88. blist = addr.to_bytes(3, byteorder="big")
  89. for b in blist:
  90. sendSingleByte(b.to_bytes(1, byteorder="big"))
  91. sendSingleByte( '\n'.encode('utf-8') )
  92. #print("block erased")
  93. def fillBuffer(page):
  94. port.read(1) # should return 'r', to indicate ready to accept command
  95. sendSingleByte( 'b'.encode('utf-8') )
  96. size = 0
  97. addr = 0
  98. blist = size.to_bytes(3, byteorder="big")
  99. for b in blist:
  100. sendSingleByte(b.to_bytes(1, byteorder="big"))
  101. blist = addr.to_bytes(3, byteorder="big")
  102. for b in blist:
  103. sendSingleByte(b.to_bytes(1, byteorder="big"))
  104. sendSingleByte( '\n'.encode('utf-8') )
  105. # command is sent, now wait for 'b' to write 256 bytes
  106. port.read(1)
  107. for i in range(256):
  108. # Slow, but accurate (needs flasher_accurate.bin):
  109. #sendSingleByteWaitForAck(page[i].to_bytes(1, byteorder="big"))
  110. # Fast, but needs finetuning on host device (needs flasher_fast.bin)
  111. sendSingleByte(page[i].to_bytes(1, byteorder="big"))
  112. #print("page sent")
  113. def writeBuffer(addr):
  114. port.read(1) # should return 'r', to indicate ready to accept command
  115. sendSingleByte( 'w'.encode('utf-8') )
  116. size = 0
  117. blist = size.to_bytes(3, byteorder="big")
  118. for b in blist:
  119. sendSingleByte(b.to_bytes(1, byteorder="big"))
  120. blist = addr.to_bytes(3, byteorder="big")
  121. for b in blist:
  122. sendSingleByte(b.to_bytes(1, byteorder="big"))
  123. sendSingleByte( '\n'.encode('utf-8') )
  124. #print("page written")
  125. def writePage(page, addr):
  126. fillBuffer(page) # fill buffer with page
  127. writeBuffer(addr) # tell FPGC to write the buffer to addr
  128. def readFlash(size, addr, filename):
  129. port.read(1) # should return 'r', to indicate ready to accept command
  130. sendSingleByte( 'r'.encode('utf-8') )
  131. blist = size.to_bytes(3, byteorder="big")
  132. for b in blist:
  133. sendSingleByte(b.to_bytes(1, byteorder="big"))
  134. blist = addr.to_bytes(3, byteorder="big")
  135. for b in blist:
  136. sendSingleByte(b.to_bytes(1, byteorder="big"))
  137. sendSingleByte( '\n'.encode('utf-8') )
  138. with open(filename, 'wb') as f:
  139. for i in tqdm(range(size)):
  140. rcv = port.read(1)
  141. f.write(rcv)
  142. #print("read done")
  143. # Writes inFile to addr.
  144. # If verify, then read and dump to outFile and diff on inFile and outFile
  145. def writeFlash(inFile, addr = 0, verify = False, outFile = "verify.bin"):
  146. # get size of inFile
  147. size = os.stat(inFile).st_size
  148. print("Going to write " + str(size) + " bytes")
  149. # erase before programming
  150. blocksToErase = -(-size // BLOCKSIZE) # number of 32KiB blocks to erase
  151. blocksErased = 0
  152. for i in range(blocksToErase):
  153. eraseBlock(i*BLOCKSIZE)
  154. blocksErased+=1
  155. print("Erased " + str(blocksErased) + " blocks")
  156. # get bytes to write
  157. ba = bytearray()
  158. # read binary file to byte array
  159. with open(inFile, "rb") as f:
  160. bytes_read = f.read()
  161. for b in bytes_read:
  162. ba.append(b)
  163. print("Writing pages")
  164. # loop in chunks of 256 bytes
  165. chunk_size= PAGESIZE
  166. for i in tqdm(range(0, len(ba), chunk_size)):
  167. chunk = ba[i:i+chunk_size]
  168. # write chunk
  169. writePage(chunk, addr)
  170. addr = addr + PAGESIZE
  171. print()
  172. # verify
  173. if verify:
  174. print("Reading data to verify")
  175. readFlash(size, 0, outFile)
  176. if os.system("diff " + inFile + " " + outFile):
  177. print("ERROR: WRITE FAILED!")
  178. else:
  179. print("Write successful")
  180. print()
  181. print("---FPGC FLASH PROGRAMMER---")
  182. if args.command == "read":
  183. readFlash(args.length, args.address, args.output)
  184. elif args.command == "write":
  185. enableVerify = False
  186. if args.verify:
  187. enableVerify = True
  188. writeFlash(args.input, args.address, enableVerify, args.verify)
  189. else:
  190. print("Command not fully implemented yet")
  191. # notify done
  192. port.read(1) # should return 'r', to indicate ready to accept command
  193. for x in range(8):
  194. sendSingleByte( 'd'.encode('utf-8') )