twist.py 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. from twisted.internet.protocol import DatagramProtocol
  2. from twisted.internet import reactor, task
  3. from twisted.internet.task import deferLater
  4. import os
  5. import time
  6. import struct
  7. import sys
  8. import hashlib
  9. import operator
  10. import functools
  11. import random
  12. class Transporter(DatagramProtocol):
  13. def __init__(self, jobs):
  14. self.udp_transport = reactor.listenUDP(9947, self)
  15. self.pending = {}
  16. self.seq = 0
  17. self.jobs = jobs
  18. self.firstjob()
  19. task.LoopingCall(self.earliest).start(0.1)
  20. reactor.run()
  21. def firstjob(self):
  22. self.jobs[0].startwork(self)
  23. def propose(self, cmd, rest):
  24. seq = self.seq
  25. self.seq += 1
  26. data = struct.pack(">HH", seq, cmd) + rest;
  27. self.pending[seq] = (time.time(), data)
  28. return seq
  29. def earliest(self):
  30. bytime = [(t, k) for (k, (t, _)) in self.pending.items()]
  31. for (t, seq) in sorted(bytime)[:32]:
  32. self.send(seq)
  33. self.pending[seq] = (time.time(), self.pending[seq][1])
  34. def datagramReceived(self, data, (host, port)):
  35. # print "received %r from %s:%d" % (data, host, port)
  36. (opcode, seq) = struct.unpack(">HH", data[:4])
  37. assert opcode == 0
  38. if seq in self.pending:
  39. del self.pending[seq]
  40. try:
  41. self.jobs[0].addresult(self, seq, data[4:])
  42. except AssertionError as e:
  43. print 'assertion failed', e
  44. reactor.stop()
  45. return
  46. print "ACK ", seq, "pending", len(self.pending)
  47. if len(self.pending) == 0:
  48. self.jobs[0].close()
  49. self.jobs = self.jobs[1:]
  50. if self.jobs != []:
  51. self.firstjob()
  52. else:
  53. reactor.stop()
  54. # self.transport.write(data, (host, port))
  55. def send(self, seq):
  56. (_, data) = self.pending[seq]
  57. # print "send %r" % data
  58. self.udp_transport.write(data, ("192.168.0.99", 947))
  59. def addresult(self, seq, payload):
  60. pass
  61. class Action(object):
  62. def addresult(self, tr, seq, payload):
  63. pass
  64. def close(self):
  65. pass
  66. class ReadRAM(Action):
  67. def startwork(self, tr):
  68. self.result = 16384 * [None]
  69. self.seqs = {}
  70. for i in range(0, 128):
  71. self.seqs[tr.propose(0, struct.pack(">H", i * 128))] = i * 128
  72. def addresult(self, tr, seq, payload):
  73. addr = self.seqs[seq]
  74. assert len(payload) == 128
  75. for i in range(128):
  76. self.result[addr + i] = ord(payload[i])
  77. def close(self):
  78. for a in range(0, 16384, 16):
  79. print ("%04x " % a) + " ".join("%02x" % x for x in self.result[a:a+16])
  80. class WriteRAM(Action):
  81. def startwork(self, tr):
  82. code = open('j1.bin').read()
  83. for i in range(0x1f80 / 128):
  84. print i
  85. o = 128 * i
  86. tr.propose(1, struct.pack(">H128s", 0x2000 + o, code[o:o+128]))
  87. class VerifyRAM(ReadRAM):
  88. def close(self):
  89. actual = "".join([chr(c) for c in self.result[0x2000:]])
  90. expected = open('j1.bin').read()
  91. l = 0x1f80
  92. assert actual[:l] == expected[:l]
  93. class Reboot(Action):
  94. def startwork(self, tr):
  95. tr.propose(2, "")
  96. class ReadFlash(Action):
  97. def startwork(self, tr):
  98. self.result = 2 * 1024 * 1024 * [None]
  99. self.seqs = {}
  100. for addr in range(0, len(self.result), 128):
  101. self.seqs[tr.propose(3, struct.pack(">I", addr))] = addr
  102. def addresult(self, tr, seq, payload):
  103. addr = self.seqs[seq]
  104. assert len(payload) == 128
  105. for i in range(128):
  106. self.result[addr + i] = ord(payload[i])
  107. def close(self):
  108. open('flash.dump', 'w').write("".join([chr(x) for x in self.result]))
  109. for a in range(0, 256, 16):
  110. print ("%04x " % a) + " ".join("%02x" % x for x in self.result[a:a+16])
  111. class EraseFlash(Action):
  112. def startwork(self, tr):
  113. tr.propose(4, "")
  114. def close(self):
  115. time.sleep(5)
  116. class WaitFlash(Action):
  117. def startwork(self, tr):
  118. self.seq = tr.propose(5, struct.pack(">I", 0))
  119. def addresult(self, tr, seq, payload):
  120. (res,) = struct.unpack(">H", payload)
  121. if res == 0:
  122. self.startwork(tr)
  123. def bitload(bitfilename):
  124. bit = open(bitfilename, "r")
  125. def getH(fi):
  126. return struct.unpack(">H", bit.read(2))[0]
  127. def getI(fi):
  128. return struct.unpack(">I", bit.read(4))[0]
  129. bit.seek(getH(bit), os.SEEK_CUR)
  130. assert getH(bit) == 1
  131. # Search for the data section in the .bit file...
  132. while True:
  133. ty = ord(bit.read(1))
  134. if ty == 0x65:
  135. break
  136. length = getH(bit)
  137. bit.seek(length, os.SEEK_CUR)
  138. fieldLength = getI(bit)
  139. return bit.read(fieldLength)
  140. # open("xxx", "w").write(bitload("j1_program.bit"))
  141. import intelhex
  142. import array
  143. class Hexfile(object):
  144. def __init__(self, filename):
  145. self.hf = intelhex.IntelHex(filename)
  146. self.hf.readfile()
  147. while (self.hf.maxaddr() % 128) != 127:
  148. self.hf[self.hf.maxaddr() + 1] = 0xff
  149. print "%x %x" % (self.hf.minaddr(), self.hf.maxaddr())
  150. def minmax(self):
  151. return (self.hf.minaddr(), self.hf.maxaddr())
  152. # The XESS CPLD bootloader runs the flash in byte mode,
  153. # and the flash is littleendian, so must do the endian
  154. # swap here
  155. def blk(self, o):
  156. b128 = array.array('B', [self.hf[o + i] for i in range(128)]).tostring()
  157. hh = array.array('H', b128)
  158. hh.byteswap()
  159. return hh.tostring()
  160. class WriteFlash(Action, Hexfile):
  161. def startwork(self, tr):
  162. for o in range(self.hf.minaddr(), self.hf.maxaddr(), 128):
  163. tr.propose(6, struct.pack(">I", o) + self.blk(o))
  164. class VerifyFlash(Action, Hexfile):
  165. def startwork(self, tr):
  166. self.seqs = {}
  167. for o in range(self.hf.minaddr(), self.hf.maxaddr(), 128):
  168. self.seqs[tr.propose(3, struct.pack(">I", o))] = o
  169. def addresult(self, tr, seq, payload):
  170. addr = self.seqs[seq]
  171. assert len(payload) == 128, 'short packet'
  172. assert self.blk(addr) == payload, "mismatch at %#x" % addr
  173. def close(self):
  174. print "Flash verified OK"
  175. class EraseSector(Action):
  176. def __init__(self, a):
  177. self.a = a
  178. def startwork(self, tr):
  179. tr.propose(7, struct.pack(">I", self.a))
  180. def close(self):
  181. time.sleep(.1)
  182. class WaitSector(Action):
  183. def __init__(self, a):
  184. self.a = a
  185. def startwork(self, tr):
  186. self.seq = tr.propose(5, struct.pack(">I", self.a))
  187. def addresult(self, tr, seq, payload):
  188. (res,) = struct.unpack(">H", payload)
  189. if res == 0:
  190. self.startwork(tr)
  191. class LoadSector(Action):
  192. def __init__(self, a, data):
  193. self.a = a
  194. self.data = data
  195. def startwork(self, tr):
  196. for o in range(0, len(self.data), 128):
  197. blk = self.data[o:o+128]
  198. if blk != (128 * chr(0xff)):
  199. tr.propose(6, struct.pack(">I", self.a + o) + blk)
  200. class DumpSector(Action):
  201. def __init__(self, a):
  202. self.a = a
  203. def startwork(self, tr):
  204. self.seqs = {}
  205. for o in [0]:
  206. self.seqs[tr.propose(3, struct.pack(">I", self.a + o))] = o
  207. def addresult(self, tr, seq, payload):
  208. addr = self.a + self.seqs[seq]
  209. assert len(payload) == 128
  210. print "result", repr(payload)
  211. # t = Transporter([WriteRAM(), VerifyRAM(), Reboot()])
  212. # t = Transporter([EraseFlash(), WaitFlash()])
  213. # sys.exit(0)
  214. erasing = [EraseFlash(), WaitFlash()]
  215. bases = [ 0 ]
  216. bases = [0, 0x80000, 0x100000, 0x180000]
  217. bases = [0x80000]
  218. # Transporter(erasing + [WriteFlash("j1_program_%x.mcs" % base) for base in bases])
  219. # Transporter([VerifyFlash("j1_program_%x.mcs" % base) for base in bases])
  220. # Transporter([EraseSector(seca), WaitSector(seca), ld, DumpSector(seca)])
  221. def loadcode(dsta, filenames):
  222. data = "".join([open(fn).read() for fn in filenames])
  223. return [EraseSector(dsta),
  224. WaitSector(dsta),
  225. LoadSector(dsta, data)]
  226. def pngstr(filename):
  227. import Image
  228. sa = array.array('B', Image.open(filename).convert("L").tostring())
  229. return struct.pack('>1024H', *sa.tolist())
  230. def erasesecs(lo, hi):
  231. r = []
  232. for s in range(lo, hi, 65536):
  233. r += [EraseSector(s), WaitSector(s)]
  234. return r
  235. def loadhex(filename):
  236. w = WriteFlash(filename)
  237. (lo, hi) = w.minmax()
  238. return erasesecs(lo, hi) + [w]
  239. def loadsprites(dsta, filenames):
  240. data = "".join([pngstr(f) for f in filenames])
  241. print "Loading %d bytes" % len(data)
  242. return erasesecs(dsta, dsta + len(data)) + [LoadSector(dsta, data)]
  243. # Transporter(loadcode(0x180000, ["j1.png.pic", "font8x8", "j1.png.chr"]) + [Reboot()])
  244. spr = ["%d.png" % (i/2) for i in range(16)]
  245. spr += ["blob.png"] * 16
  246. spr += ["fsm-32.png", "pop.png"] * 6 + ["bomb.png", "pop.png", "shot.png", "pop.png"]
  247. # Transporter(loadsprites(0x200000, spr))
  248. # Transporter(loadcode(0x190000, ["j1.bin"]) + [Reboot()])
  249. # t = Transporter([ReadFlash()])
  250. Transporter(
  251. # loadhex("j1_program_80000.mcs")
  252. loadcode(0x190000, ["j1.bin"]) + [Reboot()]
  253. )