MimasV2Config.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. import sys
  2. import serial
  3. import struct
  4. import time
  5. ERROR_FILE_TOO_LARGE = 0xEFFF0001
  6. MAX_PORTS = 100
  7. CDC_DATA_OUT_EP_SIZE = 70
  8. CDC_DATA_IN_EP_SIZE = 38
  9. IN_BUFFER_FLUSH_DELAY = 0.05
  10. IO_DIRECTION_OUT = 0
  11. IO_DIRECTION_IN = 1
  12. MODE_00 = 0x00 # Setting for SPI bus Mode 0,0
  13. MODE_01 = 0x01 # Setting for SPI bus Mode 0,1
  14. MODE_10 = 0x02 # Setting for SPI bus Mode 1,0
  15. MODE_11 = 0x03 # Setting for SPI bus Mode 1,1
  16. SPI_FOSC_64 = 0x02
  17. SMPMID = 0x00
  18. CONFIG_OUT_PACKET_SPI_OPEN = 0
  19. CONFIG_OUT_PACKET_SPI_CLOSE = 1
  20. CONFIG_OUT_PACKET_SPI_GETSTRING = 2
  21. CONFIG_OUT_PACKET_SPI_PUTSTRING = 3
  22. CONFIG_OUT_PACKET_SPI_GETSTRING_ATADDRESS = 4
  23. CONFIG_OUT_PACKET_SPI_PUTSTRING_ATADDRESS = 5
  24. CONFIG_OUT_PACKET_SPI_GET_CHAR = 6
  25. CONFIG_OUT_PACKET_SPI_PUT_CHAR = 7
  26. CONFIG_OUT_PACKET_SPI_SET_IO_DIR = 8
  27. CONFIG_OUT_PACKET_SPI_SET_IO_VALUE = 9
  28. CONFIG_OUT_PACKET_SPI_GET_IO_VALUE = 10
  29. CONFIG_OUT_PACKET_SPI_GET_ALL_IO_VALUES = 11
  30. CONFIG_IN_PACKET_STATUS = 0
  31. CONFIG_IN_PACKET_BUFFER = 1
  32. CONFIG_IO_PIN_SI = 0
  33. CONFIG_IO_PIN_SO = 1
  34. CONFIG_IO_PIN_CS = 2
  35. CONFIG_IO_PIN_CLK = 3
  36. CONFIG_IO_PIN_PROGB = 4
  37. CONFIG_IO_PIN_DONE = 5
  38. CONFIG_IO_PIN_INITB = 6
  39. IO_DIRECTION_OUT = 0
  40. IO_DIRECTION_IN = 1
  41. DEV_ID_M45PE10VMN6P = 0x114020
  42. DEV_ID_ATMEL_AT45DB021D = 0x231F
  43. DEV_ID_ATMEL_AT45DB161D = 0x261F
  44. DEV_ID_MICRON_M25P16 = 0x152020
  45. FLASH_ALGORITHM_M45PE10VMN6P = 0x01
  46. FLASH_ALGORITHM_ATMEL_DATAFLASH = 0x02
  47. FLASH_ALGORITHM_M25P16 = 0x03
  48. #Generic SPI flash commands
  49. SPI_FLASH_READ_ID_9F = 0x9F
  50. #Atmel Dataflash specific
  51. ATMEL_DATAFLASH_READ = 0x03
  52. ATMEL_DATAFLASH_READ_STATUS = 0xD7
  53. ATMEL_DATAFLASH_BUFFER_WRITE = 0x84
  54. ATMEL_DATAFLASH_PAGE_PROGRAM = 0x83
  55. #M45PE10VMN6P Specific
  56. M45PE10VMN6P_WRITE_ENABLE = 0x06
  57. M45PE10VMN6P_WRITE_DISABLE = 0x04
  58. M45PE10VMN6P_READ_ID = 0x9F
  59. M45PE10VMN6P_READ_STATUS = 0x05
  60. M45PE10VMN6P_READ = 0x03
  61. M45PE10VMN6P_FAST_READ = 0x0B
  62. M45PE10VMN6P_PAGE_WRITE = 0x0A
  63. M45PE10VMN6P_PAGE_PROGRAM = 0x02
  64. M45PE10VMN6P_PAGE_ERASE = 0xDB
  65. M45PE10VMN6P_SECTOR_ERASE = 0xD8
  66. M45PE10VMN6P_DEEP_PWR_DOWN = 0xB9
  67. M45PE10VMN6P_REL_DEEP_PWR_DOWN = 0xB9
  68. #M25P16 Specific
  69. M25P16_WRITE_ENABLE = 0x06
  70. M25P16_WRITE_DISABLE = 0x04
  71. M25P16_READ_ID = 0x9F
  72. M25P16_READ_STATUS = 0x05
  73. M25P16_READ = 0x03
  74. M25P16_FAST_READ = 0x0B
  75. M25P16_PAGE_PROGRAM = 0x02
  76. M25P16_SECTOR_ERASE = 0xD8
  77. M25P16_BULK_ERASE = 0xC7
  78. M25P16_DEEP_PWR_DOWN = 0xB9
  79. M25P16_REL_DEEP_PWR_DOWN = 0xAB
  80. #-------------------------- class MimasV2ConfigDownloader ---------------------#
  81. class MimasV2ConfigDownloader:
  82. 'Configuration downloader class'
  83. def __init__(self, Port):
  84. 'Try to open the port and initialize the object'
  85. self.PortObj = serial.Serial(Port, 19200, timeout=1)
  86. if self.PortObj is None :
  87. print("Unable to open port " + Port)
  88. exit(1)
  89. def SendData(self, Data):
  90. 'The lowest level routine to send raw data to Mimas V2'
  91. 'Returns total number of writes written'
  92. i = 0
  93. bytesWritten = 0;
  94. #Send data 30 bytes at a time. Mimas V2 can recieve maximum 30 bytes per transaction
  95. while i < len(Data):
  96. bytesWritten += self.PortObj.write(Data[i:i+30])
  97. i += 30
  98. return bytesWritten
  99. def ReadData(self, count):
  100. return self.PortObj.read(count)
  101. def SendCommand(self, Command):
  102. 'Send a command to Mimas V2'
  103. 'This routine will add padding to make all commands 70 bytes long'
  104. if(len(Command) < 70):
  105. Command += b" " * (70 - len(Command))
  106. if self.SendData(Command) == 70:
  107. return 0
  108. else:
  109. return 1
  110. def SpiOpen(self):
  111. 'Set up SPI peripheral inside PIC18 chip on Mimas V2'
  112. #Packet Structure : Sync Byte, PacketType, SpiNum, SyncMode, BusMode, SmpPhase
  113. # ~ , 0x00 , 0x01 , 0x02 , 0x00 , 0x00
  114. return self.SendCommand(b"\x7e\x00\x01\x02\x00\x00")
  115. def SpiClose(self):
  116. 'Deinitialize and free resources allocated with SpiOpen command'
  117. #Packet Structure : Sync Byte, PacketType, SpiNum
  118. # ~ , 0x01 , 0x01
  119. return self.SendCommand(b"\x7e\x01\x01")
  120. def SpiSetIoDirection(self, Io, Direction):
  121. 'Set direction of IOs that are needed for configuration process'
  122. #Packet Structure : Sync Byte, PacketType, SpiNum, Io, Direction
  123. # ~ , 0x08 , 0x01 , Io, Direction
  124. return self.SendCommand(b"\x7e\x08\x01" + struct.pack('BB', Io, Direction))
  125. def SpiSetIoValue(self, Io, Value):
  126. 'Set value of IOs that are needed for configuration process'
  127. #Packet Structure : Sync Byte, PacketType, SpiNum, Io, Value
  128. # ~ , 0x09 , 0x01 , Io, Value
  129. return self.SendCommand(b"\x7e\x09\x01" + struct.pack('BB', Io, Value))
  130. def FlushInBuffer(self):
  131. 'Flush input buffer of the port'
  132. #Sometimes flushInput fails to actually flush the contents of the buffer
  133. #unless a slight delay is given
  134. time.sleep(IN_BUFFER_FLUSH_DELAY)
  135. self.PortObj.flushInput()
  136. def CheckStatus(self, LastCmd = None):
  137. 'Checks the satus of the last command sent. Use this routine only with commands'
  138. 'that returns generic status response.'
  139. #Try to read 100 bytes from the input buffer. The maximum amount of data expected
  140. #is 38 bytes. If we receive more than 38 bytes, that means the input buffer has
  141. #response from more than one commands. This is means input buffer is not flushed
  142. #before sending the last command. Input buffer can flushed by either calling
  143. #FlushInBuffer() routine or by reading large enough data from the input buffer.
  144. #In most cases, simply calling CheckStatus() should clear the input buffer.
  145. response = mimasport.ReadData(100)
  146. print (response)
  147. if len(response) > 38:
  148. return 1
  149. else:
  150. if (response[0:1] == b'~') and (response[1:2] == struct.pack('B', CONFIG_IN_PACKET_STATUS)) and (response[3:4] == struct.pack('B', 0)):
  151. if LastCmd == None:
  152. return 0
  153. else:
  154. if response[4:5] == struct.pack('B', LastCmd):
  155. return 0
  156. else:
  157. return 1
  158. else:
  159. return 1
  160. def ToggleCS(self):
  161. 'Toggles Chip Select'
  162. #Set CS to output
  163. if self.SpiSetIoDirection(CONFIG_IO_PIN_CS, IO_DIRECTION_OUT):
  164. return 1;
  165. #De-assert CS
  166. if self.SpiSetIoValue(CONFIG_IO_PIN_CS, 1):
  167. return 1
  168. #Assert CS
  169. return self.SpiSetIoValue(CONFIG_IO_PIN_CS, 0)
  170. def SpiPutChar(self, Char):
  171. 'Writes a character to SPI port'
  172. #Packet Structure : Sync Byte, PacketType, SpiNum, Char
  173. # ~ , 0x07 , 0x01 , Char
  174. if self.SendCommand(b"\x7e\x07\x01" + struct.pack('B', Char)):
  175. return 1
  176. def SpiPutString(self, Buffer, Length):
  177. 'Writes a string/buffer to SPI port'
  178. #Packet Structure : Sync Byte, PacketType, SpiNum, Char
  179. # ~ , 0x03 , 0x01 , Length, Res0, Res1, data
  180. if self.SendCommand(b"\x7e\x03\x01" + struct.pack('B', Length) + b"\x00\x00" + Buffer[0:Length]):
  181. return 1
  182. def GetString(self, Length):
  183. 'Reads a string/buffer from SPI'
  184. #Send CONFIG_OUT_PACKET_SPI_GETSTRING command
  185. #Packet Structure : Sync Byte, PacketType, SpiNum, Length
  186. # ~ , 0x02 , 0x01 , Length
  187. if self.SendCommand(b"\x7e\x02\x01" + struct.pack('B', Length)):
  188. return (1, None)
  189. #Read the response and extract data
  190. response = self.ReadData(38)
  191. if len(response) != 38:
  192. return (1, None)
  193. return (0, response[6:6+Length])
  194. def FlashReadID9Fh(self):
  195. 'Reads flash ID using command 9Fh'
  196. #Toggle CS to get SPI flash to a known state
  197. if self.ToggleCS():
  198. return 1
  199. #Write command 9Fh
  200. if self.SpiPutChar(SPI_FLASH_READ_ID_9F):
  201. return 1
  202. #Flush input buffer
  203. self.FlushInBuffer();
  204. #Read three bytes from SPI flash
  205. status, string = self.GetString(3)
  206. if status:
  207. return None
  208. else:
  209. idTuple = struct.unpack("=I", string + b'\x00')
  210. return idTuple[0]
  211. def M25P16WriteEnable(self):
  212. 'Enable write for SPI flash M25P16'
  213. #Toggle CS to get SPI flash to a known state
  214. if self.ToggleCS():
  215. return 1
  216. #Send write enable code
  217. if self.SpiPutChar(M25P16_WRITE_ENABLE):
  218. return 1
  219. #De-assert CS
  220. if self.SpiSetIoValue(CONFIG_IO_PIN_CS, 1):
  221. return 1
  222. return 0
  223. def M25P16ReadStatus(self):
  224. 'Reads M25P16 Status register'
  225. #Toggle CS to get SPI flash to a known state
  226. if self.ToggleCS():
  227. return 1
  228. #Write M25P16_READ_STATUS command
  229. if self.SpiPutChar(M25P16_READ_STATUS):
  230. return 1
  231. #Flush input buffer
  232. self.FlushInBuffer();
  233. #Read one byte from SPI flash
  234. status, string = self.GetString(1)
  235. if status:
  236. return None
  237. else:
  238. statusTuple = struct.unpack("B", string)
  239. return int(statusTuple[0])
  240. def M25P16sectorErase(self, EndAddress):
  241. 'Erases sectors up to the sector that contains EndAddress'
  242. EndAddress |= 0xFFFF;
  243. i = 0
  244. for i in range(0, EndAddress, 0xFFFF):
  245. #Do write enable
  246. if self.M25P16WriteEnable():
  247. return 1
  248. #Toggle CS to get SPI flash to a known state
  249. if self.ToggleCS():
  250. return 1
  251. #Send Sector Erase command
  252. if self.SpiPutChar(M25P16_SECTOR_ERASE):
  253. return 1
  254. #Send address
  255. address = struct.pack("i", i)
  256. if self.SpiPutChar(address[2]):
  257. return 1
  258. if self.SpiPutChar(address[1]):
  259. return 1
  260. if self.SpiPutChar(address[0]):
  261. return 1
  262. #De-assert CS
  263. if self.SpiSetIoValue(CONFIG_IO_PIN_CS, 1):
  264. return 1
  265. #Wait for sector erase to complete
  266. while self.M25P16ReadStatus() & 0x01:
  267. time.sleep(0.01)
  268. return 0
  269. def M25P16PageProgram(self, Buffer, Address, Length):
  270. 'SPI Flash page program'
  271. if Length > 0x100:
  272. return 1
  273. #Do write enable
  274. if self.M25P16WriteEnable():
  275. return 1
  276. #Toggle CS to get SPI flash to a known state
  277. if self.ToggleCS():
  278. return 1
  279. #Send page program command
  280. if self.SpiPutChar(M25P16_PAGE_PROGRAM):
  281. return 1
  282. #Send address
  283. address = struct.pack("i", Address)
  284. if self.SpiPutChar(address[2]):
  285. return 1
  286. if self.SpiPutChar(address[1]):
  287. return 1
  288. if self.SpiPutChar(address[0]):
  289. return 1
  290. #Send data 64 bytes at a time
  291. i = 0
  292. j = 0
  293. while Length:
  294. j = 64 if Length > 64 else Length
  295. if self.SpiPutString(Buffer[i:i+64], j):
  296. return(1)
  297. i += j
  298. Length -= j;
  299. #De-assert CS
  300. if self.SpiSetIoValue(CONFIG_IO_PIN_CS, 1):
  301. return 1
  302. def M25P16VerifyFlash(self, buffer):
  303. 'Reads the contents of flash and compare with data in buffer'
  304. #Toggle CS to get SPI flash to a known state
  305. if self.ToggleCS():
  306. return 1
  307. #Send page program command
  308. if self.SpiPutChar(M25P16_READ):
  309. return 1
  310. #Send address bytes
  311. if self.SpiPutChar(0):
  312. return 1
  313. if self.SpiPutChar(0):
  314. return 1
  315. if self.SpiPutChar(0):
  316. return 1
  317. #Flush input buffer
  318. self.FlushInBuffer();
  319. readLength = len(buffer)
  320. readBuffer = b""
  321. while readLength:
  322. j = 32 if readLength > 32 else readLength
  323. status, string = self.GetString(j)
  324. if status:
  325. return 1
  326. readBuffer += string
  327. readLength -= j
  328. if readBuffer == buffer:
  329. return 0
  330. else:
  331. return 1
  332. def ConfigureMimasV2(self, FileName):
  333. 'Configures Mimas V2'
  334. #Set PROGB to output
  335. if self.SpiSetIoDirection(CONFIG_IO_PIN_PROGB, IO_DIRECTION_OUT):
  336. print ("Unable to set PROGB direction")
  337. exit(1)
  338. #Pull PROGB Low while Flash is being programmed
  339. if self.SpiSetIoValue(CONFIG_IO_PIN_PROGB, 0):
  340. print ("Unable to assert PROGB")
  341. exit(1)
  342. #Open SPI port
  343. if self.SpiOpen():
  344. print ("Unable to openSPI port")
  345. exit(1)
  346. id = self.FlashReadID9Fh()
  347. if id == None:
  348. print("Unable to read SPI Flash ID")
  349. exit(1)
  350. elif id == DEV_ID_MICRON_M25P16:
  351. print("Micron M25P16 SPI Flash detected")
  352. FlashAlgorithm = FLASH_ALGORITHM_M25P16
  353. else:
  354. print("Unknown flash part, exiting...")
  355. exit(1)
  356. #Execute device specific programming algorithm
  357. if FlashAlgorithm == FLASH_ALGORITHM_M25P16:
  358. #Open the binary file and load the contents to buffer
  359. print("Loading file " + FileName + "...")
  360. file = open(FileName, "rb")
  361. if file == None:
  362. print("Could not open file " + FileName)
  363. #Find out the size of the file
  364. file.seek(0, 2)
  365. fileSize = file.tell()
  366. fileSizeForProgressInd = fileSize
  367. #Read file in to buffer
  368. file.seek(0, 0)
  369. dataBuff = file.read(fileSize)
  370. file.close()
  371. #Erase flash sectors
  372. print("Erasing flash sectors...")
  373. if self.M25P16sectorErase(fileSize):
  374. print ("Unable to erase flash sectors")
  375. exit(1)
  376. i, j, address = 0, 0, 0
  377. while fileSize:
  378. j = 0x100 if fileSize > 0x100 else fileSize
  379. self.M25P16PageProgram(dataBuff[address:address+j], address, j)
  380. address += j
  381. fileSize -= j
  382. #Wait for page program to complete
  383. while self.M25P16ReadStatus() & 0x01:
  384. time.sleep(0.01)
  385. sys.stdout.write("Writing to flash " + str(int((address/fileSizeForProgressInd)*100)) + "% complete...\r")
  386. sys.stdout.flush()
  387. #verify the flash contents
  388. print("\nVerifying flash contents...")
  389. if self.M25P16VerifyFlash(dataBuff):
  390. print("Flash verification failed...")
  391. else:
  392. print("Flash verification successful...\nBooting FPGA...")
  393. #Set CS to input
  394. if self.SpiSetIoDirection(CONFIG_IO_PIN_CS, IO_DIRECTION_IN):
  395. return 1
  396. #De-assert PROGB
  397. if self.SpiSetIoValue(CONFIG_IO_PIN_PROGB, 1):
  398. return 1
  399. print("Done.")
  400. def __del__(self):
  401. self.PortObj.close()
  402. #-------------------------- End class MimasV2ConfigDownloader -----------------#
  403. def main():
  404. print("****************************************")
  405. print("* Numato Lab Mimas V2 Configuration Tool *")
  406. print("****************************************")
  407. if(len(sys.argv) != 3):
  408. print("ERROR: Invalid number of arguments.\n")
  409. print("Usage : mimasv2config.py <PORT> <Binary File>\n")
  410. print("PORT - The serial portcorresponds to Mimas V2 (Eg: COM1)\n")
  411. print("Binary File - Binary file to be downloaded. Please see Mimas V2")
  412. print("documentation for more details on generating binary file from")
  413. print("your design.")
  414. exit(1)
  415. MimasV2configObj = MimasV2ConfigDownloader(sys.argv[1])
  416. MimasV2configObj.ConfigureMimasV2(sys.argv[2])
  417. if __name__ == "__main__":
  418. main()