ocdconsole.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. #!/usr/bin/env python3
  2. # tools/lwl/ocdconsole.py
  3. #
  4. # Copyright (C) 2019 Dave Marples. All rights reserved.
  5. # Author: Dave Marples <dave@marples.net>
  6. #
  7. # Redistribution and use in source and binary forms, with or without
  8. # modification, are permitted provided that the following conditions
  9. # are met:
  10. #
  11. # 1. Redistributions of source code must retain the above copyright
  12. # notice, this list of conditions and the following disclaimer.
  13. # 2. Redistributions in binary form must reproduce the above copyright
  14. # notice, this list of conditions and the following disclaimer in
  15. # the documentation and/or other materials provided with the
  16. # distribution.
  17. # 3. Neither the name NuttX nor the names of its contributors may be
  18. # used to endorse or promote products derived from this software
  19. # without specific prior written permission.
  20. #
  21. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  22. # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  23. # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  24. # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  25. # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  26. # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  27. # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  28. # OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  29. # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  30. # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  31. # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  32. # POSSIBILITY OF SUCH DAMAGE.
  33. #
  34. #
  35. # Usage
  36. # =====
  37. #
  38. # No special python modules are needed, it should be possible to run the
  39. # application simply as shown below;
  40. #
  41. # ------------------------------------------
  42. # $ ./ocdconsole.py
  43. # ==Link Activated
  44. #
  45. # nsh>
  46. # nsh> help
  47. # help usage: help [-v] [<cmd>]
  48. #
  49. # ? echo exit hexdump ls mh sleep xd
  50. # cat exec help kill mb mw usleep
  51. # nsh>
  52. # ------------------------------------------
  53. #
  54. # This code is designed to be 'hardy' and will survive a shutdown and
  55. # restart of the openocd process. When your target application
  56. # changes then the location of the upword and downword may change,
  57. # so they are re-searched for again. To speed up the start process
  58. # consider putting those words at fixed locations (e.g. via the
  59. # linker file) and referencing them directly.
  60. #
  61. import os
  62. import socket
  63. import time
  64. if os.name == "nt":
  65. import msvcrt
  66. else:
  67. import select
  68. import sys
  69. import termios
  70. import tty
  71. LWL_ACTIVESHIFT = 31
  72. LWL_DNSENSESHIFT = 30
  73. LWL_UPSENSESHIFT = 29
  74. LWL_OCTVALSHIFT = 27
  75. LWL_PORTSHIFT = 24
  76. LWL_PORTMASK = 7 << LWL_PORTSHIFT
  77. LWL_SENSEMASK = 3 << LWL_UPSENSESHIFT
  78. LWL_OCTVALMASK = 3 << LWL_OCTVALSHIFT
  79. LWL_ACTIVE = 1 << LWL_ACTIVESHIFT
  80. LWL_DNSENSEBIT = 1 << LWL_DNSENSESHIFT
  81. LWL_UPSENSEBIT = 1 << LWL_UPSENSESHIFT
  82. LWL_SIG = 0x7216A318
  83. LWL_PORT_CONSOLE = 1
  84. # Memory to scan through looking for signature
  85. baseaddr = 0x20000000
  86. length = 0x8000
  87. def kbhit():
  88. """Returns True if a keypress is waiting to be read in stdin, False otherwise."""
  89. if os.name == "nt":
  90. return msvcrt.kbhit()
  91. else:
  92. dr, dw, de = select.select([sys.stdin], [], [], 0)
  93. return dr != []
  94. def dooutput(x):
  95. if x & 255 == 10:
  96. print("\r", flush=True)
  97. else:
  98. print(chr(x), end="", flush=True)
  99. ###############################################################################
  100. # Code from here to *** below was taken from GPL'ed ocd_rpc_example.py and is
  101. # available in its original form at contrib/rpc_examples in the openocd tree.
  102. #
  103. # This code was approved for re-release under BSD (licence at the head of this
  104. # file) by the original author (Andreas Ortmann, ortmann@finf.uni-hannover.de)
  105. # via email to Dave Marples on 3rd June 2019.
  106. # email ID: 15e1f0a0-9592-bd07-c996-697f44860877@finf.uni-hannover.de
  107. ###############################################################################
  108. def strToHex(data):
  109. return map(strToHex, data) if isinstance(data, list) else int(data, 16)
  110. class oocd:
  111. NL = "\x1A"
  112. def __init__(self, verbose=False):
  113. self.verbose = verbose
  114. self.tclRpcIp = "127.0.0.1"
  115. self.tclRpcPort = 6666
  116. self.bufferSize = 4096
  117. self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  118. def __enter__(self):
  119. self.sock.connect((self.tclRpcIp, self.tclRpcPort))
  120. return self
  121. def __exit__(self, type, value, traceback):
  122. try:
  123. self.send("exit")
  124. finally:
  125. self.sock.close()
  126. def send(self, cmd):
  127. """Send a command string to TCL RPC. Return the result that was read."""
  128. data = (cmd + oocd.NL).encode("utf-8")
  129. if self.verbose:
  130. print("<- ", data)
  131. self.sock.send(data)
  132. return self._recv()
  133. def _recv(self):
  134. """Read from the stream until the NL was received."""
  135. data = bytes()
  136. while True:
  137. chunk = self.sock.recv(self.bufferSize)
  138. data += chunk
  139. if bytes(oocd.NL, encoding="utf-8") in chunk:
  140. break
  141. data = data.decode("utf-8").strip()
  142. data = data[:-1] # strip trailing NL
  143. return data
  144. def readVariable(self, address):
  145. raw = self.send("%s 0x%x" % (self.mdwText, address)).split(": ")
  146. return None if (len(raw) < 2) else strToHex(raw[1])
  147. def writeVariable(self, address, value):
  148. assert value is not None
  149. self.send("mww 0x%x 0x%x" % (address, value))
  150. def testInterface(self):
  151. self.mdwText = "ocd_mdw"
  152. if self.readVariable(baseaddr) is not None:
  153. return
  154. self.mdwText = "mdw"
  155. if self.readVariable(baseaddr) is not None:
  156. return
  157. raise ConnectionRefusedError
  158. # *** Incorporated code ends ######################################################
  159. if __name__ == "__main__":
  160. def show(*args):
  161. print(*args, end="\n\n")
  162. fd = sys.stdin.fileno()
  163. old_settings = termios.tcgetattr(fd)
  164. while True:
  165. try:
  166. tty.setraw(fd)
  167. with oocd() as ocd:
  168. while True:
  169. # Find the location for the communication variables
  170. # =================================================
  171. try:
  172. ocd.testInterface()
  173. downwordaddr = 0
  174. while downwordaddr < length:
  175. if ocd.readVariable(baseaddr + downwordaddr) == LWL_SIG:
  176. break
  177. downwordaddr = downwordaddr + 4
  178. if downwordaddr >= length:
  179. print("ERROR: Cannot find signature\r")
  180. exit(1)
  181. # We have the base address, so get the variables themselves
  182. # =========================================================
  183. downwordaddr = baseaddr + downwordaddr + 4
  184. upwordaddr = downwordaddr + 4
  185. downword = LWL_ACTIVE
  186. # Now wake up the link...keep on trying if it goes down
  187. # =====================================================
  188. while True:
  189. ocd.writeVariable(downwordaddr, downword)
  190. upword = ocd.readVariable(upwordaddr)
  191. if upword & LWL_ACTIVE != 0:
  192. print("==Link Activated\r")
  193. break
  194. except (
  195. BrokenPipeError,
  196. ConnectionRefusedError,
  197. ConnectionResetError,
  198. ) as e:
  199. raise e
  200. # Now run the comms loop until something fails
  201. # ============================================
  202. try:
  203. while True:
  204. ocd.writeVariable(downwordaddr, downword)
  205. upword = ocd.readVariable(upwordaddr)
  206. if upword & LWL_ACTIVE == 0:
  207. print("\r==Link Deactivated\r")
  208. break
  209. if kbhit():
  210. charin = sys.stdin.read(1)
  211. if ord(charin) == 3:
  212. sys.exit(0)
  213. if downword & LWL_DNSENSEBIT:
  214. downword = downword & LWL_UPSENSEBIT
  215. else:
  216. downword = (
  217. downword & LWL_UPSENSEBIT
  218. ) | LWL_DNSENSEBIT
  219. downword |= (
  220. (LWL_PORT_CONSOLE << LWL_PORTSHIFT)
  221. | (1 << LWL_OCTVALSHIFT)
  222. | LWL_ACTIVE
  223. | ord(charin)
  224. )
  225. if (upword & LWL_UPSENSEBIT) != (downword & LWL_UPSENSEBIT):
  226. incomingPort = (upword & LWL_PORTMASK) >> LWL_PORTSHIFT
  227. if incomingPort == LWL_PORT_CONSOLE:
  228. incomingBytes = (
  229. upword & LWL_OCTVALMASK
  230. ) >> LWL_OCTVALSHIFT
  231. if incomingBytes >= 1:
  232. dooutput(upword & 255)
  233. if incomingBytes >= 2:
  234. dooutput((upword >> 8) & 255)
  235. if incomingBytes == 3:
  236. dooutput((upword >> 16) & 255)
  237. if downword & LWL_UPSENSEBIT:
  238. downword = downword & ~LWL_UPSENSEBIT
  239. else:
  240. downword = downword | LWL_UPSENSEBIT
  241. except (
  242. ConnectionResetError,
  243. ConnectionResetError,
  244. BrokenPipeError,
  245. ) as e:
  246. print("\r==Link Lost\r")
  247. raise e
  248. except (BrokenPipeError, ConnectionRefusedError, ConnectionResetError):
  249. time.sleep(1)
  250. continue
  251. finally:
  252. termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)