programmer.py 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. import serial
  2. from array import array
  3. class Program:
  4. def __init__(self, intelhex):
  5. self.program = b''
  6. self.secure = b''
  7. self.checksum = b''
  8. with open(intelhex, 'r') as f:
  9. mode = 0
  10. for i, l in enumerate(f.readlines()):
  11. l = l.rstrip()
  12. if not l.startswith(':') or len(l) < 11:
  13. continue
  14. size = int(l[1:3], 16)
  15. addr = int(l[3:7], 16)
  16. rtype = int(l[7:9], 16)
  17. if len(l) != 11 + size * 2:
  18. raise ValueError(
  19. f"{intelhex}:{i} invalid number of characters, given {size * 2} found {len(l) - 11}")
  20. data = bytes.fromhex(l[9:-2])
  21. csum = (-sum(bytes.fromhex(l[1:-2]))) & 0x0FF
  22. csum_given = int(l[-2:], 16)
  23. if csum != csum_given:
  24. raise ValueError(f"{intelhex}:{i} checksum failed, given 0x{csum_given.to_bytes(1, 'big').hex()} "
  25. f"calculated 0x{csum.to_bytes(1, 'big').hex()}")
  26. if rtype == 0:
  27. if mode == 0:
  28. if addr != len(self.program):
  29. raise ValueError(f"{intelhex}:{i} expected address to be continuous")
  30. self.program += data
  31. elif mode == 16:
  32. if size != 64:
  33. raise ValueError(f"{intelhex}:{i} expected data size was 64 bytes, found {size}")
  34. self.secure = data
  35. elif mode == 32:
  36. self.checksum = data
  37. elif rtype == 4:
  38. mode = int.from_bytes(data, 'big')
  39. elif rtype == 1:
  40. break
  41. class PSoC1Prog:
  42. DEVICE_IDS = {
  43. 9: "CY8C27143",
  44. 10: "CY8C27243",
  45. 11: "CY8C27443",
  46. 12: "CY8C27543",
  47. 13: "CY8C27643",
  48. 50: "CY8C24123A",
  49. 51: "CY8C24223A",
  50. 52: "CY8C24423A",
  51. 2225: "CY8C23533",
  52. 2224: "CY8C23433",
  53. 2226: "CY8C23033",
  54. 23: "CY8C21123",
  55. 24: "CY8C21223",
  56. 25: "CY8C21323",
  57. 54: "CY8C21234",
  58. 2103: "CY8C21312",
  59. 55: "CY8C21334",
  60. 56: "CY8C21434",
  61. 2112: "CY8C21512",
  62. 64: "CY8C21534",
  63. 73: "CY8C21634",
  64. 1848: "CY8CTMG110_32LTXI",
  65. 1849: "CY8CTMG110_00PVXI",
  66. 1592: "CY8CTST110_32LTXI",
  67. 1593: "CY8CTST110_00PVXI",
  68. }
  69. def __init__(self, serial_port):
  70. self.ser = serial.Serial(port=serial_port, baudrate=9600, timeout=0.1, write_timeout=1)
  71. self.ser.write(b'\n')
  72. x = self.ser.read_until(b'> ')
  73. def reinitialise(self):
  74. self.ser.write(b'i')
  75. res = self.ser.read_until('\r\n> ')
  76. def reset_device(self):
  77. self.ser.write(b'r')
  78. res = self.ser.read_until('\r\n> ')
  79. def get_device_id(self):
  80. self.ser.write(b'd')
  81. res = self.ser.read_until('\r\n> ')
  82. return int(res[:-4], 16)
  83. def get_firmware_id(self):
  84. self.ser.write(b'f')
  85. res = self.ser.read_until('\r\n> ')
  86. return res[:-4]
  87. def erase_memory(self):
  88. self.ser.write(b'e')
  89. self.ser.read_until('\r\n> ')
  90. def read_checksum(self):
  91. self.ser.write(b'c')
  92. res = self.ser.read_until('\r\n> ')
  93. return int(res[:-4], 16).to_bytes(2, 'big')
  94. def read_memory(self, n=64, offset=0x80):
  95. # read n x 64 memory blocks
  96. blocks = b''
  97. for i in range(n):
  98. self.ser.write(b'r' + array('B', [i, offset]).tobytes())
  99. x = self.ser.read_until('\r\n> ')
  100. block = self._read_blk()
  101. blocks += block
  102. print(f"\rReading 0x{i.to_bytes(1, 'big').hex()} [{(i+1)/64*100:.2f}%]", end='')
  103. print('')
  104. return blocks
  105. def get_device_name(self):
  106. devid = self.get_device_id()
  107. return self.DEVICE_IDS.get(devid, devid.to_bytes(2, 'big').hex())
  108. def _write_blk(self, data):
  109. if len(data) != 64:
  110. raise ValueError("Data is not 64 bytes")
  111. self.ser.write(b't')
  112. self.ser.write(data)
  113. x = self.ser.read_until('\r\n> ')
  114. return
  115. def _read_blk(self):
  116. self.ser.write(b's')
  117. blk = self.ser.read(64)
  118. x = self.ser.read_until('\r\n> ')
  119. return blk
  120. def write_program(self, data):
  121. self.erase_memory()
  122. for i in range(len(data)//64):
  123. self._write_blk(data[i*64:(i+1)*64])
  124. self.ser.write(b'w' + array('B', [i]).tobytes())
  125. x = self.ser.read_until('\r\n> ')
  126. print(f"\rWriting 0x{i.to_bytes(1, 'big').hex()} [{(i+1)/64*100:.2f}%]", end='')
  127. print('')
  128. def write_secure(self, data):
  129. self._write_blk(data)
  130. self.ser.write(b'x')
  131. x = self.ser.read_until('\r\n> ')
  132. def close(self):
  133. self.ser.close()
  134. if __name__ == '__main__':
  135. import argparse
  136. import sys
  137. parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter)
  138. parser.add_argument("port", help="Serial port")
  139. parser.add_argument("cmd", choices=['flash', 'checksum', 'device', 'read', 'erase', 'reset'],
  140. help="Command to run\n" +
  141. "flash - write .hex to device\n" +
  142. "checksum - returns program checksum from device\n" +
  143. "device - returns device name or identification hex\n" +
  144. "read - dumps device program to file\n" +
  145. "erase - deletes all devices program memory\n" +
  146. "reset - restarts device\n"
  147. )
  148. parser.add_argument("-i", "--input", help="Input intel hex file for flashing")
  149. parser.add_argument("-o", "--output", help="Output binary for memory dump")
  150. parser.add_argument("--offset", type=int, default=0x80, help="Memory dump read address offset")
  151. parser.add_argument("--count", type=int, default=64, help="Memory dump read count")
  152. parser.add_argument("--read", action="store_true", help="Read back program when flashing to double check")
  153. parser.add_argument("--reset", action="store_true", help="Reset device after command is complete")
  154. parser.add_argument("--init", type=bool, default=True, help="Reinitialise programming mode on device")
  155. args = parser.parse_args()
  156. prog = PSoC1Prog(args.port)
  157. if args.init:
  158. prog.reinitialise()
  159. if args.cmd == 'flash':
  160. if args.input is None:
  161. print("PSoC1_Prog: input is not specified", file=sys.stderr)
  162. exit(1)
  163. ifile = Program(args.input)
  164. print("PSoC1_Prog: erasing memory")
  165. prog.erase_memory()
  166. print("PSoC1_Prog: writing program")
  167. prog.write_program(ifile.program)
  168. print("PSoC1_Prog: writing secure")
  169. prog.write_secure(ifile.secure)
  170. print("PSoC1_Prog: checking checksum")
  171. csum = prog.read_checksum()
  172. if csum != ifile.checksum:
  173. print(f"PSoC1_Prog: checksum mismatch, device={csum.hex()} file={ifile.checksum.hex()}", file=sys.stderr)
  174. exit(1)
  175. if args.read:
  176. mem_data = prog.read_memory()
  177. if mem_data != ifile.program:
  178. print(f"PSoC1_Prog: written program does not match one on device", file=sys.stderr)
  179. exit(1)
  180. print("PSoC1_Prog: success")
  181. elif args.cmd == 'checksum':
  182. print(prog.read_checksum().hex())
  183. elif args.cmd == 'device':
  184. print(prog.get_device_name())
  185. elif args.cmd == 'read':
  186. if args.output is None:
  187. print("PSoC1_Prog: output is not specified", file=sys.stderr)
  188. exit(1)
  189. with open(args.output, 'wb') as f:
  190. f.write(prog.read_memory(args.count, args.offset))
  191. print("PSoC1_Prog: success")
  192. elif args.cmd == 'erase':
  193. prog.erase_memory()
  194. print("PSoC1_Prog: success")
  195. elif args.cmd == 'reset':
  196. prog.reset_device()
  197. exit(0)
  198. if args.reset:
  199. prog.reset_device()
  200. exit(0)