In-system programming tool for LPC microcontrollers
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

lpc.py 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. # Copyright 2017 Clayton G. Hobbs
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. """Interfaces for in-system programming of LPC microcontrollers"""
  15. import struct
  16. import time
  17. from enum import Enum
  18. import serial
  19. from alpaca_isp.exceptions import *
  20. DEFAULT_BAUDRATE = 115200
  21. DEFAULT_CLOCK = 12000
  22. DEFAULT_TIMEOUT = 0.1
  23. class LPC:
  24. """Interface for LPC in-system programming"""
  25. def __init__(self, tty, baudrate=DEFAULT_BAUDRATE,
  26. clock=DEFAULT_CLOCK, timeout=DEFAULT_TIMEOUT):
  27. # If the first parameter is an LPC, initialize self from that
  28. if isinstance(tty, LPC):
  29. o = tty
  30. self._tty = o._tty
  31. self._baudrate = o._baudrate
  32. self._clock = o._clock
  33. self._timeout = o._timeout
  34. self._echo = o._echo
  35. try:
  36. self._uart = o._uart
  37. except AttributeError:
  38. # If there's no o._uart, that's not a problem
  39. pass
  40. # Otherwise, initialize from the parameters
  41. else:
  42. self._tty = tty
  43. self._baudrate = baudrate
  44. self._clock = clock
  45. self._timeout = timeout
  46. self._echo = True
  47. def open(self):
  48. """Open the serial port to communicate with the microcontroller"""
  49. self._uart = serial.Serial(self._tty, baudrate=self._baudrate,
  50. timeout=self._timeout)
  51. def _readline(self):
  52. """Read a line terminated with b'\r\n'"""
  53. s = b""
  54. while True:
  55. c = self._uart.read(1)
  56. if not c:
  57. # If we timed out, give up
  58. raise RecvTimeout(s)
  59. s += c
  60. if s.endswith(b"\r\n"):
  61. return s
  62. def _writeline(self, line, plain=False):
  63. """Write a line to the microcontroller and read the echoed response
  64. If plain is True, the command is taken to be raw binary data.
  65. """
  66. self._uart.write(line)
  67. self._uart.flush()
  68. # If echo is disabled, don't try to read back what we sent
  69. if not self.echo:
  70. return
  71. # Read the response, raising the exception if there is one
  72. if plain:
  73. response = self._uart.read(len(line))
  74. else:
  75. response = self._readline()
  76. # If we got the wrong response, raise an exception
  77. if response != line:
  78. raise ISPError("Wrong text echoed: {}".format(response))
  79. def _send_command_raw(self, cmd):
  80. """Send a command to the microcontroller, returning bytes"""
  81. self._writeline(cmd)
  82. return self._readline()
  83. def _send_command(self, cmd):
  84. """Send a command to the microcontroller, returning the result"""
  85. r = self._send_command_raw(cmd)
  86. r = ReturnCode(int(r))
  87. if r != ReturnCode.CMD_SUCCESS:
  88. raise ISPError(r)
  89. return r
  90. def enter_isp(self, delay=0.01):
  91. """Enter ISP mode by controlling the DTR (/RST) and RTS (/ISP) lines
  92. This operation is performed synchronously, with delays.
  93. """
  94. self._uart.rts = True
  95. time.sleep(delay)
  96. self._uart.dtr = True
  97. time.sleep(delay)
  98. self._uart.dtr = False
  99. time.sleep(delay)
  100. self._uart.rts = False
  101. def synchronize(self, verbose=False, max_tries=None):
  102. """Begin communication with the microcontroller
  103. If verbose is True, prints a . for every synchronization attempt.
  104. If max_tries is an integer, attempt to synchronize at most that many
  105. times before failing by raising RecvTimeout.
  106. """
  107. # Synchronize with the MCU
  108. while True:
  109. # Send a ?
  110. self._uart.write(b"?")
  111. self._uart.flush()
  112. if verbose:
  113. print(".", end="", flush=True)
  114. # Receive a response
  115. try:
  116. s = self._readline()
  117. except RecvTimeout:
  118. if max_tries is not None:
  119. max_tries -= 1
  120. if max_tries <= 0:
  121. raise
  122. continue
  123. # If we got the right response, break
  124. if s == b"Synchronized\r\n":
  125. break
  126. # Tell the MCU we've synchronized
  127. s = self._send_command_raw(b"Synchronized\r\n")
  128. # Next, it should say OK, at which point we're done synchronizing
  129. if s != b"OK\r\n":
  130. raise ISPError("Wrong response during synchronization")
  131. # Send clock frequency in kHz
  132. s = self._send_command_raw("{:d}\r\n".format(self._clock).encode(
  133. "utf-8"))
  134. # Next, it should say OK
  135. if s != b"OK\r\n":
  136. raise ISPError("Wrong response during synchronization")
  137. def close(self):
  138. """Close the serial port"""
  139. self._uart.close()
  140. def unlock(self, code="23130"):
  141. """Unlock the flash write, erase, and go commands"""
  142. self._send_command("U {}\r\n".format(code).encode("utf-8"))
  143. @property
  144. def baudrate(self):
  145. """The baud rate used for communication"""
  146. return self._uart.baudrate
  147. @baudrate.setter
  148. def baudrate(self, br):
  149. self._send_command("B {} {}\r\n".format(br,
  150. self._uart.stopbits).encode("utf-8"))
  151. # Update the baud rate for our UART
  152. self._uart.baudrate = br
  153. @property
  154. def stopbits(self):
  155. """The number of stop bits used for communication"""
  156. return self._uart.stopbits
  157. @stopbits.setter
  158. def stopbits(self, sb):
  159. self._send_command("B {} {}\r\n".format(self._uart.baudrate,
  160. sb).encode("utf-8"))
  161. # Update the number of stop bits for our UART
  162. self._uart.stopbits = sb
  163. @property
  164. def echo(self):
  165. """Whether the microcontroller echoes characters back to the host"""
  166. return self._echo
  167. @echo.setter
  168. def echo(self, setting):
  169. setting = bool(setting)
  170. self._send_command("A {}\r\n".format(int(setting)).encode("utf-8"))
  171. self._echo = setting
  172. def write_ram(self, start, data, count=None):
  173. """Write count bytes from data to RAM at the given start address
  174. Start and count must be multiples of four. If count is not specified,
  175. len(data) is used.
  176. """
  177. # Get the length of the data we're writing
  178. if count is None:
  179. count = len(data)
  180. # Ask to write data
  181. self._send_command("W {} {}\r\n".format(start, count).encode("utf-8"))
  182. # Send the data
  183. # NOTE: this is right for LPC8xx chips, not others
  184. self._writeline(data[:count], plain=True)
  185. return
  186. def read_memory(self, start, count):
  187. """Read count bytes starting at the given address
  188. Start and count must be multiples of four.
  189. """
  190. self._send_command("R {} {}\r\n".format(start, count).encode("utf-8"))
  191. return self._uart.read(count)
  192. def prepare_write(self, start=None, end=None):
  193. """Prepare the the given flash sector(s) for write operations
  194. If end is not specified, only the start sector is prepared.
  195. If neither start nor end is specified, prepares all flash sectors.
  196. """
  197. if start is None and end is None:
  198. start = 0
  199. end = len(self._chip.sectors) - 1
  200. elif end is None:
  201. end = start
  202. self._send_command("P {} {}\r\n".format(start, end).encode("utf-8"))
  203. def copy_ram_to_flash(self, flash, ram, count):
  204. """Copy count bytes from RAM to flash
  205. The flash address should be a 64 byte boundary. Count should be a
  206. power of two in [64, 1024].
  207. """
  208. self._send_command("C {} {} {}\r\n".format(flash, ram, count).encode(
  209. "utf-8"))
  210. def go(self, address=0, mode="T"):
  211. """Jump to the given address, in the given mode of execution
  212. Of course, this function generally causes the ISP command handler to
  213. stop running, so it is typically appropriate to follow this with a call
  214. to LPC.close.
  215. """
  216. self._writeline("G {} {}\r\n".format(address, mode).encode("utf-8"))
  217. def erase(self, start=None, end=None):
  218. """Erase the given flash sector(s)
  219. If end is not specified, only the start sector is erased.
  220. If neither start nor end is specified, erases all flash sectors.
  221. """
  222. if start is None and end is None:
  223. start = 0
  224. end = len(self._chip.sectors) - 1
  225. elif end is None:
  226. end = start
  227. self._send_command("E {} {}\r\n".format(start, end).encode("utf-8"))
  228. def blank_check(self, start, end=None):
  229. """Check if the given flash sectors are blank
  230. If end is not specified, only the start sector is checked.
  231. Returns None if the sector is blank, or a tuple containing the offset
  232. and value of the first non-blank word location if the sector is not
  233. blank. If CRP is enabled, the offset and value are always reported as
  234. zero.
  235. """
  236. if end is None:
  237. end = start
  238. try:
  239. self._send_command("I {} {}\r\n".format(start, end).encode(
  240. "utf-8"))
  241. except ISPError as e:
  242. # Return a tuple for SECTOR_NOT_BLANK
  243. if e.args[0] == ReturnCode.SECTOR_NOT_BLANK:
  244. offset = int(self._readline())
  245. value = int(self._readline())
  246. return (offset, value)
  247. raise
  248. @property
  249. def part_id(self):
  250. """The identification number for the part"""
  251. self._send_command(b"J\r\n")
  252. return int(self._readline())
  253. @property
  254. def boot_code_version(self):
  255. """The boot code version number (major, minor)"""
  256. self._send_command(b"K\r\n")
  257. major = int(self._readline())
  258. minor = int(self._readline())
  259. return (major, minor)
  260. def compare(self, addr1, addr2, count):
  261. """Compart count bytes starting from the two addresses
  262. Both addresses should be on word boundaries, and count should be a
  263. multiple of four.
  264. Returns None if the two blocks are equal, or the byte offset of the
  265. first mismatched word if they are not.
  266. """
  267. try:
  268. self._send_command("M {} {} {}\r\n".format(addr1, addr2,
  269. count).encode("utf-8"))
  270. except ISPError as e:
  271. # Return an offset for COMPARE_ERROR
  272. if e.args[0] == ReturnCode.COMPARE_ERROR:
  273. return int(self._readline())
  274. raise
  275. @property
  276. def uid(self):
  277. """The microcontroller's unique ID, as bytes"""
  278. self._send_command(b"N\r\n")
  279. words = []
  280. for _ in range(4):
  281. words.append(int(self._readline()))
  282. return struct.pack("<4I", *words)
  283. def read_crc32(self, start, count):
  284. """Compute the CRC checksum of a black of RAM or flash
  285. Start must be on a word boundary, and count must be a multiple of four.
  286. """
  287. self._send_command("S {} {}\r\n".format(start, count).encode("utf-8"))
  288. return int(self._readline())
  289. def flash_hex(self, ihex, verbose=False):
  290. """Write an IntelHex object to flash
  291. Only the sectors that have any data in ihex are changed. These sectors
  292. are erased and written with the new data, destroying anything that was
  293. in the sector before.
  294. """
  295. # Assert that we have enough RAM to hold the largest flash sector
  296. assert (((self._chip.ram_start + 0x400*self._chip.ram)
  297. - self._chip.ram_base) >= max(self._chip.sectors))
  298. # Assert that the largest sector is no larger than max_copy
  299. assert (self._chip.max_copy >= max(self._chip.sectors))
  300. # NOTE: of course both of these assertions wouldn't be necessary if we
  301. # were more careful below, but for the chips I'm using they hold, so
  302. # I'm in no rush to make this perfect.
  303. # Get the sectors we're rewriting
  304. sectors_used = self._chip.sectors_used(ihex.segments())
  305. # Erase the sectors that aren't already blank
  306. for sector in sectors_used:
  307. if self.blank_check(sector) is not None:
  308. if verbose:
  309. print("Erasing sector {}".format(sector))
  310. self.prepare_write(sector)
  311. self.erase(sector)
  312. # Write sectors, with 0 at the end
  313. ss = self._chip.sector_segments
  314. if 0 in sectors_used:
  315. sectors_used.remove(0)
  316. sectors_used.append(0)
  317. for sector in sectors_used:
  318. if verbose:
  319. print("Writing sector {}".format(sector))
  320. # Get the data we'll be writing
  321. secdat = ihex.tobinstr(start=ss[sector][0], end=ss[sector][1]-1)
  322. # If we're writing sector 0, set the checksum
  323. if sector == 0:
  324. iv = struct.unpack('<7I', secdat[0:28])
  325. cs = struct.pack('<I', (-(sum(iv) % 2**32)) & 0xFFFFFFFF)
  326. secdat = secdat[:28] + cs + secdat[32:]
  327. # Write data to RAM
  328. bs = 0x100
  329. for i in range(0, len(secdat), bs):
  330. self.write_ram(self._chip.ram_base+i, secdat[i:i+bs])
  331. # Copy RAM to flash
  332. self.prepare_write(sector)
  333. self.copy_ram_to_flash(ss[sector][0], self._chip.ram_base,
  334. len(secdat))
  335. def verify(self, ihex, verbose=False):
  336. """Verify that the data in an IntelHex object is stored in flash"""
  337. # Get the sectors we're verifying
  338. sectors_used = self._chip.sectors_used(ihex.segments())
  339. # Verify segments
  340. ss = self._chip.sector_segments
  341. for sector in sectors_used:
  342. if verbose:
  343. print("Verifying sector {}... ".format(sector), end="")
  344. # Get the data we're verifying
  345. secdat = ihex.tobinstr(start=ss[sector][0], end=ss[sector][1]-1)
  346. # Read data from flash
  347. try:
  348. flash = self.read_memory(ss[sector][0],
  349. self._chip.sectors[sector])
  350. except ISPError as e:
  351. if e.args[0] == ReturnCode.CODE_READ_PROTECTION_ENABLED:
  352. print()
  353. raise
  354. # Verify the sector
  355. if sector == 0:
  356. # Avoid comparing first 64 bytes of sector 0 because these are
  357. # remapped to flash boot sector
  358. secdat = secdat[64:]
  359. flash = flash[64:]
  360. if secdat != flash:
  361. print("failed!")
  362. raise ISPError("Flash verification failed")
  363. else:
  364. print()
  365. class LPC8xx(LPC):
  366. """Interface for LPC8xx in-system programming"""
  367. def __init__(self, lpc, chip):
  368. """Initialize an LPC8xx from an existing LPC object"""
  369. super().__init__(lpc)
  370. self._chip = chip
  371. class ReturnCode(Enum):
  372. """LPC ISP return codes
  373. From UM10800, section 25.6.1.16.
  374. """
  375. CMD_SUCCESS = 0
  376. INVALID_COMMAND = 1
  377. SRC_ADDR_ERROR = 2
  378. DST_ADDR_ERROR = 3
  379. SRC_ADDR_NOT_MAPPED = 4
  380. DST_ADDR_NOT_MAPPED = 5
  381. COUNT_ERROR = 6
  382. INVALID_SECTOR = 7
  383. SECTOR_NOT_BLANK = 8
  384. SECTOR_NOT_PREPARED_FOR_WRITE_OPERATION = 9
  385. COMPARE_ERROR = 10
  386. BUSY = 11
  387. PARAM_ERROR = 12
  388. ADDR_ERROR = 13
  389. ADDR_NOT_MAPPED = 14
  390. CMD_LOCKED = 15
  391. INVALID_CODE = 16
  392. INVALID_BAUD_RATE = 17
  393. INVALID_STOP_BIT = 18
  394. CODE_READ_PROTECTION_ENABLED = 19