main.py 16 KB

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