Move LWL python script to tools/lwl and add README.txt

This commit is contained in:
Alan C. Assis
2021-01-15 17:34:52 -03:00
committed by Xiang Xiao
parent f40389b359
commit f62ff8d938
2 changed files with 92 additions and 57 deletions
+89
View File
@@ -0,0 +1,89 @@
Console over Lightweight Link
=============================
LWL is a Lightweight bidirectional communication between target and debug host
without any need for additional hardware.
It works with openOCD and other debuggers that are capable of reading and
writing memory while the target is running...it should run with JLink
for example, if you've got the SDK and modify this file accordingly.
Principle of operation is simple; An 'upword' of 32 bits communicates
from the target to the host, a 'downword' of the same size runs in the
opposite direction. These two words can be in any memory that is
read/write access for both the target and the debug host. A simple ping
pong handshake protocol over these words allows up/down link communication.
On the upside no additional integration is needed. On the downside it may be
necessary to feed lwl with cycles to poll for changes in the downword,
depending on the use case. For the case of a simple console, that's not
needed.
For convenience these communication locations are automatically discovered
from the RAM by searching through it. Just define downwordaddr and
upwordaddr if you want to work with fixed locations.
Bit configuration
-----------------
Downword (Host to target);
A D U VV XXX O2 O1 O0
A 31 1 - Service Active (Set by host)
D 30 1 - Downsense (Toggled when there is data)
U 29 1 - Upsense ack (Toggled to acknowledge receipt of uplink data)
VV 28-27 2 - Valid Octets (Number of octets valid in the message)
XXX 26-24 3 - Port in use (Type of the message)
O2 23-16 8 - Octet 2
O1 15-08 8 - Octet 1
O0 07-00 8 - Octet 0
Upword (Target to Host);
A 31 1 - Service Active (Set by device)
D 30 1 - Downsense ack (Toggled to acknowledge receipt of downlink data)
U 29 1 - Upsense (Toggled when there is data)
VV 28-27 2 - Valid upword octets
XXX 26-24 3 - Port in use (Type of the message)
O2 23-16 8 - Octet 2
O1 15-08 8 - Octet 1
O0 07-00 8 - Octet 0
Port 1 is used for Console. No other ports are currently defined.
Usage
=====
No special python modules are needed, it should be possible to run the
application simply as shown below;
------------------------------------------
$ ./ocdconsole.py
==Link Activated
nsh>
nsh> help
help usage: help [-v] [<cmd>]
? echo exit hexdump ls mh sleep xd
cat exec help kill mb mw usleep
nsh>
------------------------------------------
This code is designed to be 'hardy' and will survive a shutdown and
restart of the openocd process. When your target application
changes then the location of the upword and downword may change,
so they are re-searched for again. To speed up the start process
consider putting those words at fixed locations (e.g. via the
linker file) and referencing them directly.
Future work/Improvements
========================
Currently the lwl driver on NuttX side is doing polling, but for
better performance it could use interrupts to detect when the memory
position was modified to read the data.
It also will avoid using busy waiting inside the driver, look at
nuttx/arch/arm/src/common/arm_lwl_console.c for more information.
+264
View File
@@ -0,0 +1,264 @@
#!/usr/bin/env python3
# tools/lwl/ocdconsole.py
#
# Copyright (C) 2019 Dave Marples. All rights reserved.
# Author: Dave Marples <dave@marples.net>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
# 3. Neither the name NuttX nor the names of its contributors may be
# used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
#
# Usage
# =====
#
# No special python modules are needed, it should be possible to run the
# application simply as shown below;
#
# ------------------------------------------
# $ ./ocdconsole.py
# ==Link Activated
#
# nsh>
# nsh> help
# help usage: help [-v] [<cmd>]
#
# ? echo exit hexdump ls mh sleep xd
# cat exec help kill mb mw usleep
# nsh>
# ------------------------------------------
#
# This code is designed to be 'hardy' and will survive a shutdown and
# restart of the openocd process. When your target application
# changes then the location of the upword and downword may change,
# so they are re-searched for again. To speed up the start process
# consider putting those words at fixed locations (e.g. via the
# linker file) and referencing them directly.
#
LWL_ACTIVESHIFT = 31
LWL_DNSENSESHIFT = 30
LWL_UPSENSESHIFT = 29
LWL_OCTVALSHIFT = 27
LWL_PORTSHIFT = 24
LWL_PORTMASK = (7<<LWL_PORTSHIFT)
LWL_SENSEMASK = (3<<LWL_UPSENSESHIFT)
LWL_OCTVALMASK = (3<<LWL_OCTVALSHIFT)
LWL_ACTIVE = (1<<LWL_ACTIVESHIFT)
LWL_DNSENSEBIT = (1<<LWL_DNSENSESHIFT)
LWL_UPSENSEBIT = (1<<LWL_UPSENSESHIFT)
LWL_SIG = 0x7216A318
LWL_PORT_CONSOLE = 1
# Memory to scan through looking for signature
baseaddr = 0x20000000
length = 0x8000
import time
import socket
import os
if os.name == 'nt':
import msvcrt
else:
import sys, select, termios, tty
def kbhit():
''' Returns True if a keypress is waiting to be read in stdin, False otherwise.
'''
if os.name == 'nt':
return msvcrt.kbhit()
else:
dr,dw,de = select.select([sys.stdin], [], [], 0)
return dr != []
def dooutput(x):
if (x&255==10):
print("\r",flush=True)
else:
print(chr(x),end="",flush=True)
###############################################################################
# Code from here to *** below was taken from GPL'ed ocd_rpc_example.py and is
# available in its original form at contrib/rpc_examples in the openocd tree.
#
# This code was approved for re-release under BSD (licence at the head of this
# file) by the original author (Andreas Ortmann, ortmann@finf.uni-hannover.de)
# via email to Dave Marples on 3rd June 2019.
# email ID: 15e1f0a0-9592-bd07-c996-697f44860877@finf.uni-hannover.de
###############################################################################
def strToHex(data):
return map(strToHex, data) if isinstance(data, list) else int(data, 16)
class oocd:
NL = '\x1A'
def __init__(self, verbose=False):
self.verbose = verbose
self.tclRpcIp = "127.0.0.1"
self.tclRpcPort = 6666
self.bufferSize = 4096
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
def __enter__(self):
self.sock.connect((self.tclRpcIp, self.tclRpcPort))
return self
def __exit__(self, type, value, traceback):
try:
self.send("exit")
finally:
self.sock.close()
def send(self, cmd):
"""Send a command string to TCL RPC. Return the result that was read."""
data = (cmd + oocd.NL).encode("utf-8")
if self.verbose:
print("<- ", data)
self.sock.send(data)
return self._recv()
def _recv(self):
"""Read from the stream until the NL was received."""
data = bytes()
while True:
chunk = self.sock.recv(self.bufferSize)
data += chunk
if bytes(oocd.NL, encoding="utf-8") in chunk:
break
data = data.decode("utf-8").strip()
data = data[:-1] # strip trailing NL
return data
def readVariable(self, address):
raw = self.send("%s 0x%x" % (self.mdwText,address)).split(": ")
return None if (len(raw) < 2) else strToHex(raw[1])
def writeVariable(self, address, value):
assert value is not None
self.send("mww 0x%x 0x%x" % (address, value))
def testInterface(self):
self.mdwText="ocd_mdw"
if (self.readVariable(baseaddr)!=None):
return
self.mdwText="mdw"
if (self.readVariable(baseaddr)!=None):
return
raise ConnectionRefusedError
# *** Incorporated code ends ######################################################
if __name__ == "__main__":
def show(*args):
print(*args, end="\n\n")
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
while True:
try:
tty.setraw(fd)
with oocd() as ocd:
while True:
# Find the location for the communication variables
# =================================================
try:
ocd.testInterface()
downwordaddr=0
while (downwordaddr<length):
if (ocd.readVariable(baseaddr+downwordaddr)==LWL_SIG):
break
downwordaddr=downwordaddr+4
if (downwordaddr>=length):
print("ERROR: Cannot find signature\r")
exit(1)
# We have the base address, so get the variables themselves
# =========================================================
downwordaddr=baseaddr+downwordaddr+4
upwordaddr=downwordaddr+4
downword=LWL_ACTIVE
# Now wake up the link...keep on trying if it goes down
# =====================================================
while True:
ocd.writeVariable(downwordaddr, downword)
upword = ocd.readVariable(upwordaddr)
if (upword&LWL_ACTIVE!=0):
print("==Link Activated\r")
break
except (BrokenPipeError, ConnectionRefusedError, ConnectionResetError) as e:
raise e
# Now run the comms loop until something fails
# ============================================
try:
while True:
ocd.writeVariable(downwordaddr, downword)
upword = ocd.readVariable(upwordaddr)
if (upword&LWL_ACTIVE==0):
print("\r==Link Deactivated\r")
break
if kbhit():
charin = sys.stdin.read(1)
if (ord(charin)==3):
sys.exit(0)
if (downword&LWL_DNSENSEBIT):
downword=(downword&LWL_UPSENSEBIT)
else:
downword=(downword&LWL_UPSENSEBIT)|LWL_DNSENSEBIT
downword|=(LWL_PORT_CONSOLE<<LWL_PORTSHIFT)|(1<<LWL_OCTVALSHIFT)|LWL_ACTIVE|ord(charin)
if ((upword&LWL_UPSENSEBIT)!=(downword&LWL_UPSENSEBIT)):
incomingPort=(upword&LWL_PORTMASK)>>LWL_PORTSHIFT
if (incomingPort==LWL_PORT_CONSOLE):
incomingBytes=(upword&LWL_OCTVALMASK)>>LWL_OCTVALSHIFT
if (incomingBytes>=1): dooutput(upword&255);
if (incomingBytes>=2): dooutput((upword>>8)&255);
if (incomingBytes==3): dooutput((upword>>16)&255);
if (downword&LWL_UPSENSEBIT):
downword = downword&~LWL_UPSENSEBIT
else:
downword = downword|LWL_UPSENSEBIT
except (ConnectionResetError, ConnectionResetError, BrokenPipeError) as e:
print("\r==Link Lost\r")
raise e
except (BrokenPipeError, ConnectionRefusedError, ConnectionResetError) as e:
time.sleep(1)
continue
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)