main.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  1. """
  2. # TOP2049 Open Source programming suite
  3. #
  4. # Copyright (c) 2009-2017 Michael Buesch <m@bues.ch>
  5. #
  6. # This program is free software; you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation; either version 2 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License along
  17. # with this program; if not, write to the Free Software Foundation, Inc.,
  18. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  19. """
  20. import sys
  21. if sys.version_info[0] < 3:
  22. print("FATAL: TOPrammer requires Python version 3.x. Please install Python 3.x")
  23. sys.exit(1)
  24. # TOPrammer version stamp
  25. VERSION_MAJOR = 0
  26. VERSION_MINOR = 19
  27. VERSION = "%d.%d" % (VERSION_MAJOR, VERSION_MINOR)
  28. from .bitfile import *
  29. from .util import *
  30. import time
  31. import re
  32. from .hardware_access_usb import *
  33. from .top_devices import *
  34. from .chips import *
  35. from .user_interface import *
  36. class FoundDev(object):
  37. def __init__(self, toptype, devIdentifier, busdata=None):
  38. self.toptype = toptype
  39. self.devIdentifier = devIdentifier
  40. self.busdata = busdata
  41. class TOP(object):
  42. # Supported programmer types
  43. TYPE_TOP2049 = "TOP2049"
  44. def __init__(self, devIdentifier=None, verbose=0,
  45. forceLevel=0, noqueue=False, usebroken=False,
  46. forceBitfileUpload=False,
  47. userInterface=ConsoleUserInterface()):
  48. self.verbose = verbose
  49. self.forceLevel = forceLevel
  50. self.forceBitfileUpload = forceBitfileUpload
  51. self.usebroken = usebroken
  52. self.userInterface = userInterface
  53. self.hw = None
  54. self.chip = None
  55. # Find the device
  56. devices = self.findDevices()
  57. if devIdentifier:
  58. devices = [ d for d in devices
  59. if d.devIdentifier.lower() == devIdentifier.lower() ]
  60. if not devices:
  61. raise TOPException("TOP programmer device not found!")
  62. foundDev = devices[0] # Select first
  63. if noqueue:
  64. self.printWarning("WARNING: Command queuing disabled. " +\
  65. "Hardware access will be _really_ slow.")
  66. self.initializeProgrammer(foundDev, noqueue)
  67. def getProgrammerType(self):
  68. "Returns the TYPE_TOPxxxx"
  69. return self.topType
  70. def initializeChip(self, chipID, assignedChipOptions=()):
  71. "Initialize the programmer for a chip"
  72. # If a chip is initialized, shut it down first.
  73. if self.chip:
  74. self.shutdownChip()
  75. # Find the implementation of the chip.
  76. descriptor = ChipDescription.findOne(chipID, self.usebroken)
  77. self.chip = descriptor.chipImplClass.createInstance(
  78. top = self,
  79. chipDescription = descriptor,
  80. assignedChipOptions = assignedChipOptions)
  81. ok = self.configureFPGA(descriptor.bitfile, descriptor.runtimeID)
  82. if not ok:
  83. self.chip = None
  84. raise TOPException("Did not find BIT-file for chip implementation %s" % chipID)
  85. def shutdownChip(self):
  86. if self.chip:
  87. self.chip.shutdownChip()
  88. self.flushCommands()
  89. self.chip = None
  90. def resetChip(self):
  91. if self.chip:
  92. self.chip.shutdownChip()
  93. self.flushCommands()
  94. def getChip(self):
  95. "Get the chip. May return None"
  96. return self.chip
  97. def checkChip(self):
  98. if not self.chip:
  99. raise TOPException("Target chip not selected")
  100. def getForceLevel(self):
  101. return self.forceLevel
  102. def progressMeterInit(self, meterId, message, nrSteps):
  103. if self.verbose >= 1:
  104. self.userInterface.progressMeterInit(meterId, message, nrSteps)
  105. def progressMeterFinish(self, meterId):
  106. if self.verbose >= 1:
  107. self.userInterface.progressMeterFinish(meterId)
  108. def progressMeter(self, meterId, step):
  109. if self.verbose >= 1:
  110. self.userInterface.progressMeter(meterId, step)
  111. def printWarning(self, message):
  112. if self.verbose >= 0:
  113. self.flushCommands()
  114. self.userInterface.warningMessage(message)
  115. def printInfo(self, message):
  116. if self.verbose >= 1:
  117. self.flushCommands()
  118. self.userInterface.infoMessage(message)
  119. def printDebug(self, message):
  120. if self.verbose >= 2:
  121. self.flushCommands()
  122. self.userInterface.debugMessage(message)
  123. @classmethod
  124. def __usbdev2toptype(cls, usbdev):
  125. "Returns the TOP type of the USB device. None, if this is not a TOP device."
  126. try:
  127. toptype = {
  128. (0x2471, 0x0853): TOP.TYPE_TOP2049,
  129. }[ (usbdev.idVendor, usbdev.idProduct) ]
  130. except (KeyError) as e:
  131. return None
  132. return toptype
  133. @classmethod
  134. def findDevices(cls):
  135. """Rescan all busses for TOP devices.
  136. Returns a list of FoundDev()"""
  137. usbFound = HardwareAccessUSB.scan(cls.__usbdev2toptype)
  138. devices = []
  139. for i, ud in enumerate(usbFound):
  140. toptype = cls.__usbdev2toptype(ud.usbdev)
  141. devices.append(
  142. FoundDev(toptype,
  143. "usb:%s:%d" % (toptype, i),
  144. ud)
  145. )
  146. return devices
  147. def initializeProgrammer(self, foundDev, noQueue):
  148. "Initialize the hardware"
  149. self.shutdownProgrammer()
  150. if foundDev.toptype == self.TYPE_TOP2049:
  151. self.hw = top2049_hardware_access.HardwareAccess(
  152. foundUSBDev = foundDev.busdata,
  153. noQueue = noQueue,
  154. doRawDump = (self.verbose >= 3))
  155. self.vcc = top2049_vcc_layouts.VCCLayout(self)
  156. self.vpp = top2049_vpp_layouts.VPPLayout(self)
  157. self.gnd = top2049_gnd_layouts.GNDLayout(self)
  158. else:
  159. assert(0)
  160. self.topType = foundDev.toptype
  161. versionRegex = (
  162. (r"top2049\s+ver\s*(\d+\.\d+)", self.TYPE_TOP2049),
  163. )
  164. # This is the first hardware access. Try several times since the programmer is in an unknown state.
  165. for _ in range(5):
  166. try:
  167. versionString = self.hw.readVersionString()
  168. break
  169. except TOPException as e:
  170. time.sleep(0.05)
  171. else:
  172. raise TOPException("Could not read version string from hardware")
  173. for (regex, t) in versionRegex:
  174. if t != self.topType:
  175. continue
  176. m = re.match(regex, versionString, re.IGNORECASE)
  177. if m:
  178. self.topVersion = m.group(1)
  179. break
  180. else:
  181. raise TOPException("Connected TOP programmer '" + versionString +\
  182. "' is not supported by Toprammer, yet")
  183. self.printInfo("Initializing the " + self.topType + " version " + self.topVersion)
  184. self.hw.hardwareInit()
  185. def shutdownProgrammer(self):
  186. if self.hw:
  187. self.hw.shutdown()
  188. self.hw = None
  189. self.topType = None
  190. self.topVersion = None
  191. def __readBitfileID(self):
  192. self.cmdFPGARead(0xFD)
  193. self.cmdFPGARead(0xFE)
  194. self.cmdFPGARead(0xFF)
  195. data = self.cmdReadBufferReg(3)
  196. gotID = byte2int(data[0]) | (byte2int(data[1]) << 8)
  197. if gotID == 0xFEFD or gotID == 0xFFFF:
  198. gotID = 0
  199. gotRev = byte2int(data[2])
  200. if gotRev == 0xFF:
  201. gotRev = 0
  202. return (gotID, gotRev)
  203. def __bitfileUpload(self, requiredRuntimeID):
  204. (requiredID, requiredRevision) = requiredRuntimeID
  205. if requiredID and requiredRevision and not self.forceBitfileUpload:
  206. # Check if the bitfile is already uploaded.
  207. (gotID, gotRev) = self.__readBitfileID()
  208. if gotID == requiredID and gotRev == requiredRevision:
  209. self.printDebug("Bitfile %s ID 0x%04X Rev 0x%02X is already uploaded." %\
  210. (self.bitfile.getFilename(), gotID, gotRev))
  211. return
  212. self.printDebug("Current runtime ID 0x%04X Rev 0x%02X. Uploading new bitfile..." %\
  213. (gotID, gotRev))
  214. self.printDebug("Uploading bitfile %s..." % self.bitfile.getFilename())
  215. self.hw.FPGAInitiateConfig()
  216. data = self.bitfile.getPayload()
  217. chunksz = self.hw.getFPGAMaxConfigChunkSize()
  218. chunksz = chunksz if chunksz > 0 else len(data)
  219. for i in range(0, len(data), chunksz):
  220. self.hw.FPGAUploadConfig(i, data[i : i + chunksz])
  221. self.flushCommands()
  222. if requiredID and requiredRevision:
  223. # Check the uploaded ID
  224. (gotID, gotRev) = self.__readBitfileID()
  225. if gotID != requiredID or gotRev != requiredRevision:
  226. raise TOPException("bit-upload: The bitfile upload succeed, "
  227. "but the read ID or revision is invalid. "
  228. "(Got 0x%04X/0x%02X, but expected 0x%04X/0x%02X)" %\
  229. (gotID, gotRev, requiredID, requiredRevision))
  230. def configureFPGA(self, bitfileName, runtimeIDs):
  231. """Upload and configure the FPGA.
  232. bitfileName -> The name of the bitfile to upload
  233. runtimeIDs -> bottom-half ID tuple: (majorID, minorID)"""
  234. # Get the bitfile path.
  235. bitfilePath = bitfileFind(bitfileName)
  236. if not bitfilePath:
  237. return False
  238. # Open and parse the bitfile.
  239. self.bitfile = Bitfile()
  240. self.bitfile.parseFile(bitfilePath)
  241. # Initialize the hardware.
  242. self.__bitfileUpload(runtimeIDs)
  243. return True
  244. def readSignature(self):
  245. """Reads the chip signature and returns it."""
  246. self.printDebug("Reading signature from chip...")
  247. self.checkChip()
  248. sig = self.chip.readSignature()
  249. self.flushCommands()
  250. self.printDebug("Done reading %d bytes." % len(sig))
  251. return sig
  252. def eraseChip(self):
  253. """Erase the chip."""
  254. self.printDebug("Erasing chip...")
  255. self.checkChip()
  256. self.chip.erase()
  257. self.flushCommands()
  258. def testChip(self):
  259. """Run a unit-test on the chip."""
  260. self.printDebug("Running chip unit-test...")
  261. self.checkChip()
  262. self.chip.test()
  263. self.flushCommands()
  264. self.printInfo("Chip unit-test terminated successfully.")
  265. def readProgmem(self):
  266. """Reads the program memory image and returns it."""
  267. self.printDebug("Reading program memory from chip...")
  268. self.checkChip()
  269. image = self.chip.readProgmem()
  270. self.flushCommands()
  271. self.printDebug("Done reading %d bytes." % len(image))
  272. return image
  273. def writeProgmem(self, image):
  274. """Writes a program memory image to the chip."""
  275. self.printDebug("Writing %d bytes of program memory to chip..." % len(image))
  276. self.checkChip()
  277. self.chip.writeProgmem(image)
  278. self.flushCommands()
  279. self.printDebug("Done writing image.")
  280. def readEEPROM(self):
  281. """Reads the EEPROM image and returns it."""
  282. self.printDebug("Reading EEPROM from chip...")
  283. self.checkChip()
  284. image = self.chip.readEEPROM()
  285. self.flushCommands()
  286. self.printDebug("Done reading %d bytes." % len(image))
  287. return image
  288. def writeEEPROM(self, image):
  289. """Writes an EEPROM image to the chip."""
  290. self.printDebug("Writing %d bytes of EEPROM to chip..." % len(image))
  291. self.checkChip()
  292. self.chip.writeEEPROM(image)
  293. self.flushCommands()
  294. self.printDebug("Done writing image.")
  295. def readFuse(self):
  296. """Reads the fuses image and returns it."""
  297. self.printDebug("Reading fuses from chip...")
  298. self.checkChip()
  299. image = self.chip.readFuse()
  300. self.flushCommands()
  301. self.printDebug("Done reading %d bytes." % len(image))
  302. return image
  303. def writeFuse(self, image):
  304. """Writes a fuses image to the chip."""
  305. self.printDebug("Writing %d bytes of fuses to chip..." % len(image))
  306. self.checkChip()
  307. self.chip.writeFuse(image)
  308. self.flushCommands()
  309. self.printDebug("Done writing image.")
  310. def readLockbits(self):
  311. """Reads the Lock bits image and returns it."""
  312. self.printDebug("Reading lock-bits from chip...")
  313. self.checkChip()
  314. image = self.chip.readLockbits()
  315. self.flushCommands()
  316. self.printDebug("Done reading %d bytes." % len(image))
  317. return image
  318. def writeLockbits(self, image):
  319. """Writes a Lock bits image to the chip."""
  320. self.printDebug("Writing %d bytes of lock-bits to chip..." % len(image))
  321. self.checkChip()
  322. self.chip.writeLockbits(image)
  323. self.flushCommands()
  324. self.printDebug("Done writing image.")
  325. def readRAM(self):
  326. """Reads the RAM and returns it."""
  327. self.printDebug("Reading RAM from chip...")
  328. self.checkChip()
  329. image = self.chip.readRAM()
  330. self.flushCommands()
  331. self.printDebug("Done reading %d bytes." % len(image))
  332. return image
  333. def writeRAM(self, image):
  334. """Writes the RAM image to the chip."""
  335. self.printDebug("Writing %d bytes of RAM to the chip..." % len(image))
  336. self.checkChip()
  337. self.chip.writeRAM(image)
  338. self.flushCommands()
  339. self.printDebug("Done writing the image.")
  340. def readUserIdLocation(self):
  341. """Reads the User ID Location and returns it."""
  342. self.printDebug("Reading UIL from chip...")
  343. self.checkChip()
  344. image = self.chip.readUserIdLocation()
  345. self.flushCommands()
  346. self.printDebug("Done reading %d bytes." % len(image))
  347. return image
  348. def writeUserIdLocation(self, image):
  349. """Writes the User ID Location image to the chip."""
  350. self.printDebug("Writing %d bytes to UIL of the chip..." % len(image))
  351. self.checkChip()
  352. self.chip.writeUserIdLocation(image)
  353. self.flushCommands()
  354. self.printDebug("Done writing the image.")
  355. def cmdDelay(self, seconds):
  356. """Send a delay request to the device. Note that this causes the
  357. programmer to execute the delay. For a host-delay, use hostDelay()"""
  358. self.hw.delay(seconds)
  359. def hostDelay(self, seconds):
  360. """Flush all commands and delay the host computer for 'seconds'"""
  361. self.flushCommands(seconds)
  362. def getOscillatorHz(self):
  363. """Returns the FPGA oscillator frequency, in Hz.
  364. The oscillator is connected to the FPGA clk pin."""
  365. return self.hw.getOscillatorHz()
  366. def getBufferRegSize(self):
  367. """Returns the size (in bytes) of the buffer register."""
  368. return self.hw.getBufferRegSize()
  369. def cmdReadBufferReg(self, nrBytes=-1):
  370. """Read the buffer register. Returns nrBytes (default all bytes)."""
  371. regSize = self.getBufferRegSize()
  372. if nrBytes < 0:
  373. nrBytes = regSize
  374. assert(nrBytes <= regSize)
  375. if not nrBytes:
  376. return b""
  377. return self.hw.readBufferReg(nrBytes)
  378. def cmdReadBufferReg8(self):
  379. """Read a 8bit value from the buffer register."""
  380. stat = self.cmdReadBufferReg(1)
  381. stat = byte2int(stat[0])
  382. return stat
  383. def cmdReadBufferReg16(self):
  384. """Read a 16bit value from the buffer register."""
  385. stat = self.cmdReadBufferReg(2)
  386. stat = byte2int(stat[0]) | (byte2int(stat[1]) << 8)
  387. return stat
  388. def cmdReadBufferReg24(self):
  389. """Read a 24bit value from the buffer register."""
  390. stat = self.cmdReadBufferReg(3)
  391. stat = byte2int(stat[0]) | (byte2int(stat[1]) << 8) | (byte2int(stat[2]) << 16)
  392. return stat
  393. def cmdReadBufferReg32(self):
  394. """Read a 32bit value from the buffer register."""
  395. stat = self.cmdReadBufferReg(4)
  396. stat = byte2int(stat[0]) | (byte2int(stat[1]) << 8) | \
  397. (byte2int(stat[2]) << 16) | (byte2int(stat[3]) << 24)
  398. return stat
  399. def cmdReadBufferReg48(self):
  400. """Read a 48bit value from the buffer register."""
  401. stat = self.cmdReadBufferReg(6)
  402. stat = byte2int(stat[0]) | (byte2int(stat[1]) << 8) | \
  403. (byte2int(stat[2]) << 16) | (byte2int(stat[3]) << 24) | \
  404. (byte2int(stat[4]) << 32) | (byte2int(stat[5]) << 40)
  405. return stat
  406. def cmdFPGARead(self, address):
  407. """Read a byte from the FPGA at address into the buffer register."""
  408. self.hw.FPGARead(address)
  409. def cmdFPGAWrite(self, address, byte):
  410. """Write a byte to an FPGA address."""
  411. return self.hw.FPGAWrite(address, byte)
  412. def cmdLoadGNDLayout(self, layout):
  413. """Load the GND configuration into the programmer."""
  414. self.hw.loadGNDLayout(layout)
  415. def cmdSetVPPVoltage(self, voltage):
  416. """Set the VPP voltage. voltage is a floating point voltage number."""
  417. self.hw.setVPPVoltage(voltage)
  418. def cmdLoadVPPLayout(self, layout):
  419. """Load the VPP configuration into the programmer."""
  420. self.hw.loadVPPLayout(layout)
  421. def cmdSetVCCVoltage(self, voltage):
  422. """Set the VCC voltage. voltage is a floating point voltage number."""
  423. self.hw.setVCCVoltage(voltage)
  424. def cmdLoadVCCLayout(self, layout):
  425. """Load the VCC configuration into the shift registers."""
  426. self.hw.loadVCCLayout(layout)
  427. def cmdEnableZifPullups(self, enable):
  428. """Enable the ZIF socket signal pullups."""
  429. self.hw.enableZifPullups(enable)
  430. def flushCommands(self, sleepSeconds=0):
  431. """Flush command queue and optionally sleep for 'sleepSeconds'."""
  432. self.hw.flushCommands(sleepSeconds)