arch/arm/src/common/up_lwl_console.c: Add support for a 'Lightweight Link' protocol between a target and debugger for use when you need a console but the target doesn't have a spare serial port or other available resource.

This commit is contained in:
Dave Marples 2019-06-03 07:31:17 -06:00 committed by Gregory Nutt
parent 04ea439121
commit a2def2a4d1
10 changed files with 743 additions and 11 deletions

View file

@ -29,9 +29,13 @@ nuttx/
|- arch/
| |
| |- arm/
| | |- src
| | |- <a href="https://bitbucket.org/nuttx/nuttx/src/master/arch/arm/src/lpc214x/README.txt" target="_blank">lpc214x/README.txt</a>
| | `- <a href="https://bitbucket.org/nuttx/nuttx/src/master/arch/arm/src/stm32l4/README.txt" target="_blank">stm32l4/README.txt</a>
| | `- src
| | |- common
| | | `- <a href="https://bitbucket.org/nuttx/nuttx/src/master/arch/arm/src/common/README_lwl_console.txt" target="_blank"><b>README_lwl_console.txt</b></a>
| | |- lpc214x
| | | `- <a href="https://bitbucket.org/nuttx/nuttx/src/master/arch/arm/src/lpc214x/README.txt" target="_blank">README.txt</a>
| | `- stm32l4
| | `- <a href="https://bitbucket.org/nuttx/nuttx/src/master/arch/arm/src/stm32l4/README.txt" target="_blank">stm32l4/README.txt</a>
| |- renesas/
| | |- include/
| | | `-<a href="https://bitbucket.org/nuttx/nuttx/src/master/arch/renesas/include/README.txt" target="_blank">README.txt</a>

View file

@ -1707,8 +1707,12 @@ nuttx/
| |
| |- arm/
| | `- src
| | |- lpc214x/README.txt
| | `- stm32l4/README.txt
| | |- common
| | | `- README_lwl_console.txt
| | |- lpc214x
| | | `-README.txt
| | `- stm32l4
| | `- README.txt
| |- renesas/
| | |- include/
| | | `-README.txt

View file

@ -813,6 +813,19 @@ config ARM_SEMIHOSTING_HOSTFS
---help---
Mount HostFS through semihosting.
config ARM_LWL_CONSOLE
bool "Lightweight Link Console Support"
default n
depends on DEV_CONSOLE && ARCH_CHIP_STM32
---help---
Use the lightweight link console which provides console over a
debug channel by means of shared memory. A terminal application
for openocd as the debugger is available in tools/ocdconsole.py.
Currently only available for STM32 architectures, but easily
added to other ARM architectures be addd up_low_console.c to the
architecture Make.defs file.
if ARCH_CORTEXM0
source arch/arm/src/armv6-m/Kconfig
endif

View file

@ -0,0 +1,59 @@
The file up_lwl_console.c implements a 'Lightweight Link' protocol between
a target and debugger for use when you need a console but the target doesn't
have a spare serial port or other available resource. This implements a
new console type which uses two words of memory for data exchange.
It is not particularly efficient because of the various compromises that are
made (polling in busy loops, mostly) but it works well enough to give you
something where you previously had nothing...typically the case when you're
bring up a new CPU, or when the hardware designer thought the softies could
cope without a logging port. It has an advantage over semi-hosting in that
it doesn't put the target into debug mode while it's running, so you've got
some hope of maintaining real time semantics. To be clear, for output only
use you'd be better off with SWO if you've got it available!
There is a terminal program in python(*) for the host side in
tools/ocdconsole.py for use with openocd...the NuttX side functionality is
not dependent on a specific debugger, the only requirement on it being that
the debugger can watch and modify a memory location on the target while it is executing.
Typical use is;
$ tools/ocdconsole.py
==Link Activated
NuttShell (NSH)
nsh> help
help usage: help [-v] [<cmd>]
? echo exit hexdump ls mh sleep xd
cat exec help kill mb mw usleep
nsh>
On the target side it's transparent, and is just a console;
nsh> ls /dev
/dev:
console
null
ttyS0
nsh> echo "Hello World" > /dev/console
Hello World
nsh>
CPU load on the host is surprisingly low given that the polling loop is
continuous (probably due to the fact that openocd is spending most of it's
time waiting for messages to/from the debug port on the target). When not
actively doing anything there's no load on the target, but waiting for input
is done in a busy polled loop (so the thread is effectively busy-locked)
and output busy-waits for the previous message to be collected before it
sends the next one.
For now I've only made it available on stm32, but it should only be a case
of changing the Kconfig and Make.defs for other arm CPUs to make it
available for them too. Moving beyond arm needs knowledge of the targets
that I don't have.
If anyone fancies extending this proof-of-concept to full Segger-RTT-style
functionality then drop me a note, there are plenty of ways to improve
performance.

View file

@ -204,7 +204,9 @@ void up_initialize(void)
* serial driver).
*/
#if defined(CONFIG_DEV_LOWCONSOLE)
#if defined (CONFIG_ARM_LWL_CONSOLE)
lwlconsole_init();
#elif defined(CONFIG_DEV_LOWCONSOLE)
lowconsole_init();
#elif defined(CONFIG_CONSOLE_SYSLOG)
syslog_console_init();

View file

@ -63,7 +63,11 @@
# undef CONFIG_DEV_LOWCONSOLE
# undef CONFIG_RAMLOG_CONSOLE
#else
# if defined(CONFIG_RAMLOG_CONSOLE)
# if defined(CONFIG_ARM_LWL_CONSOLE)
# undef USE_SERIALDRIVER
# undef USE_EARLYSERIALINIT
# undef CONFIG_DEV_LOWCONSOLE
# elif defined(CONFIG_RAMLOG_CONSOLE)
# undef USE_SERIALDRIVER
# undef USE_EARLYSERIALINIT
# undef CONFIG_DEV_LOWCONSOLE
@ -299,7 +303,7 @@ EXTERN uint32_t _eramfuncs; /* Copy destination end address in RAM */
****************************************************************************/
/****************************************************************************
* Public Functions
* Public Function Prototypes
****************************************************************************/
#ifndef __ASSEMBLY__
@ -449,10 +453,18 @@ void up_earlyserialinit(void);
# define up_earlyserialinit()
#endif
#ifdef CONFIG_ARM_LWL_CONSOLE
/* Defined in src/common/up_lwl_console.c */
void lwlconsole_init(void);
#elif defined(CONFIG_DEV_LOWCONSOLE)
/* Defined in drivers/lowconsole.c */
#ifdef CONFIG_DEV_LOWCONSOLE
void lowconsole_init(void);
#else
# define lowconsole_init()
#endif

View file

@ -0,0 +1,327 @@
/****************************************************************************
* drivers/serial/lwlconsole.c
*
* Copyright (C) 2019 Gregory Nutt. 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.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/arch.h>
#include <nuttx/fs/fs.h>
#include <syscall.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Lightweight Link (lwl)
* ======================
*
* Lightweight bidirectional communication between target and debug host
* without any need for additional hardware.
*
* Works with openOCD and other debuggers that are capable of reading and
* writing memory while the target is running.
*
* 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.
*
* Bit configuration
* -----------------
*
* Downword (Host to target);
*
* A D U VV XXX
*
* 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
*
*/
/* Protocol bits */
#define LWL_GETACTIVE(x) (((x) & (1 << 31)) != 0)
#define LWL_ACTIVE(x) (((x)&1) << 31)
#define LWL_DNSENSEBIT (1 << 30)
#define LWL_DNSENSE(x) ((x)&LWL_DNSENSEBIT)
#define LWL_UPSENSEBIT (1 << 29)
#define LWL_UPSENSE(x) ((x)&LWL_UPSENSEBIT)
#define LWL_SENSEMASK (3 << 29)
#define LWL_GETOCTVAL(x) (((x) >> 27) & 3)
#define LWL_OCTVAL(x) (((x)&3) << 27)
#define LWL_GETPORT(x) (((x) >> 24) & 7)
#define LWL_PORT(x) (((x)&7) << 24)
#define LWL_PORT_CONSOLE 1
#define ID_SIG 0x7216A318
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static ssize_t lwlconsole_read(struct file *filep, char *buffer,
size_t buflen);
static ssize_t lwlconsole_write(struct file *filep, const char *buffer,
size_t buflen);
static int lwlconsole_ioctl(struct file *filep, int cmd, unsigned long arg);
/****************************************************************************
* Private Data
****************************************************************************/
static struct
{
uint32_t sig; /* Location signature */
volatile uint32_t downword; /* Host to Target word */
uint32_t upword; /* Target to Host word */
} g_d =
{
.sig = ID_SIG
};
static const struct file_operations g_consoleops =
{
NULL, /* open */
NULL, /* close */
lwlconsole_read, /* read */
lwlconsole_write, /* write */
NULL, /* seek */
lwlconsole_ioctl, /* ioctl */
NULL /* poll */
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
,
NULL /* unlink */
#endif
};
/****************************************************************************
* Private Functions
****************************************************************************/
static bool linkactive(void)
{
return (LWL_GETACTIVE(g_d.downword) != 0);
}
static bool writeword(uint32_t newupword)
{
/* Check link is active */
if (!linkactive())
{
return false;
}
/* Spin waiting for previous data to be collected */
while (LWL_UPSENSE(g_d.downword) != LWL_UPSENSE(g_d.upword))
{
}
/* Load new data, toggling UPSENSE bit to show it is new */
g_d.upword = LWL_DNSENSE(g_d.upword) | newupword |
(LWL_UPSENSE(g_d.upword) ? 0 : LWL_UPSENSEBIT);
return true;
}
static bool write8bits(uint8_t port, uint8_t val)
{
/* Prepare new word */
uint32_t newupword = LWL_ACTIVE(true) | LWL_OCTVAL(1) |
LWL_PORT(port) | (val & 0xff);
return writeword(newupword);
}
static bool write16bits(uint8_t port, uint32_t val)
{
/* Prepare new word */
uint32_t newupword = LWL_ACTIVE(true) | LWL_OCTVAL(2) |
LWL_PORT(port) | (val & 0xffff);
return writeword(newupword);
}
static bool write24bits(uint8_t port, uint32_t val)
{
/* Prepare new word */
uint32_t newupword = LWL_ACTIVE(true) | LWL_OCTVAL(3) |
LWL_PORT(port) | (val & 0xffffff);
return writeword(newupword);
}
static bool read8bits(uint8_t port, uint8_t * store)
{
if (LWL_DNSENSE(g_d.downword) == LWL_DNSENSE(g_d.upword))
{
return false;
}
*store = g_d.downword & 255;
/* Flip the bit to indicate the datum is read */
g_d.upword = (g_d.upword & ~LWL_DNSENSEBIT) | LWL_DNSENSE(g_d.downword);
return true;
}
/****************************************************************************
* Name: lwlconsole_ioctl
****************************************************************************/
static int lwlconsole_ioctl(struct file *filep, int cmd, unsigned long arg)
{
return -ENOTTY;
}
/****************************************************************************
* Name: lwlconsole_read
****************************************************************************/
static ssize_t lwlconsole_read(struct file *filep, char *buffer,
size_t buflen)
{
if (buflen == 0 || !linkactive())
{
return 0;
}
while (!read8bits(LWL_PORT_CONSOLE, (uint8_t *) buffer))
{
}
return 1;
}
/****************************************************************************
* Name: lwlconsole_write
****************************************************************************/
static ssize_t lwlconsole_write(struct file *filep, const char *buffer,
size_t buflen)
{
uint32_t oc = 0;
while (buflen)
{
switch (buflen)
{
case 0:
return oc;
case 1:
if (write8bits(LWL_PORT_CONSOLE, buffer[0]))
{
oc++;
buffer++;
buflen--;
}
break;
case 2:
if (write16bits(LWL_PORT_CONSOLE, buffer[0] | (buffer[1] << 8)))
{
oc += 2;
buffer += 2;
buflen -= 2;
}
break;
default:
if (write24bits(LWL_PORT_CONSOLE, buffer[0] |
(buffer[1] << 8) | (buffer[2] << 16)))
{
oc += 3;
buffer += 3;
buflen -= 3;
}
break;
}
}
return oc;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: lwlconsole_init
****************************************************************************/
void lwlconsole_init(void)
{
g_d.upword = 0;
(void)register_driver("/dev/console", &g_consoleops, 0666, NULL);
}

View file

@ -54,6 +54,10 @@ ifeq ($(CONFIG_ARMV7M_STACKCHECK),y)
CMN_CSRCS += up_stackcheck.c
endif
ifeq ($(CONFIG_ARM_LWL_CONSOLE),y)
CMN_CSRCS += up_lwl_console.c
endif
ifeq ($(CONFIG_ARMV7M_LAZYFPU),y)
CMN_ASRCS += up_lazyexception.S
else

View file

@ -6,8 +6,7 @@
config DEV_LOWCONSOLE
bool "Low-level console support"
default n
depends on ARCH_LOWPUTC
depends on DEV_CONSOLE
depends on ARCH_LOWPUTC && DEV_CONSOLE
---help---
Use the simple, low-level, write-only serial console driver (minimal support)

308
tools/ocdconsole.py Executable file
View file

@ -0,0 +1,308 @@
#!/usr/bin/env python3
#
# 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.
#
#
# 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
# nessessary 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 convinence 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.
#
# Use
# ===
#
# 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("ocd_mdw 0x%x" % 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))
# *** 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:
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)