Drastic measures to work around missed interrupts -- must be a better way

git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@1860 42af7a65-404d-4744-a932-0658087f49c3
This commit is contained in:
patacongo 2009-06-07 15:48:01 +00:00
parent 2b29f8dfc8
commit eba07de989
4 changed files with 189 additions and 50 deletions

View File

@ -758,7 +758,10 @@
* arch/arm/src/str71x: Serial output is now correct and timer interrupts are
working. The test at configs/olimex-strp711/ostest passes. This means that
the basic STR-P711 port is complete.
* configs/olimex-strp711/nsh: Add a NuttShell (NSH) configuration for the
STR-P711.
* configs/olimex-strp711/nsh: Add and verifed a NuttShell (NSH) configuration
for the STR-P711.
* arch/arm/str71x/str71x_serial.c: The STR711 interrupt driven serial driver
finally works after some extradinary measures to handle missed interrupts.
NSH is fully functional on the Olimex STR-P711 board.

View File

@ -8,7 +8,7 @@
<tr align="center" bgcolor="#e4e4e4">
<td>
<h1><big><font color="#3c34ec"><i>NuttX RTOS</i></font></big></h1>
<p>Last Updated: June 05, 2009</p>
<p>Last Updated: June 07, 2009</p>
</td>
</tr>
</table>
@ -781,7 +781,8 @@
This port boots and passes the OS test (examples/ostest).
The port is complete and verified. As of NuttX 0.3.17, the port includes:
timer interrupts, serial console, USB driver, and SPI-based MMC/SD card
support. A verified NuttShell (NSH) configuration is also available.
support. A verified NuttShell <a href="NuttShell.html">(NSH)</a>
configuration is also available.
</p>
</td>
</tr>
@ -800,10 +801,11 @@
</p>
<p>
<b>STATUS:</b>
Integration is complete on the basic port (boot logic, system time, serial console),
The board boots and passes the OS test with console output visible on UART0.
Additional drivers are needed: SPI, USB, to name two. It should not take too much
more effort to get this port up and running.
Integration is complete on the basic port (boot logic, system time, serial console).
Two configurations have been verified: (1) The board boots and passes the OS test
with console output visible on UART0, and the NuttShell <a href="NuttShell.html">(NSH)</a>
is fully functional with interrupt driven serial console. An SPI driver is available
but untested. Additional features are needed: USB driver, MMC/SD support, to name two.
</p>
</td>
</tr>
@ -1425,8 +1427,11 @@ nuttx-0.4.8 2009-xx-xx Gregory Nutt &lt;spudmonkey@racsa.co.cr&gt;
* arch/arm/src/str71x: Serial output is now correct and timer interrupts are
working. The test at configs/olimex-strp711/ostest passes. This means that
the basic STR-P711 port is complete.
* configs/olimex-strp711/nsh: Add a NuttShell (NSH) configuration for the
STR-P711.
* configs/olimex-strp711/nsh: Add and verifed a NuttShell (NSH) configuration
for the STR-P711.
* arch/arm/str71x/str71x_serial.c: The STR711 interrupt driven serial driver
finally works after some extradinary measures to handle missed interrupts.
NSH is fully functional on the Olimex STR-P711 board.
pascal-0.1.3 2009-xx-xx Gregory Nutt &lt;spudmonkey@racsa.co.cr&gt;

View File

@ -42,7 +42,9 @@
#include <unistd.h>
#include <semaphore.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <wdog.h>
#include <debug.h>
#include <nuttx/irq.h>
#include <nuttx/arch.h>
@ -195,6 +197,11 @@
# warning "No CONFIG_UARTn_SERIAL_CONSOLE Setting"
#endif
/* I am problems with lost UART interrupts. Could this just be my hardware? */
#define CONFIG_UART_LOSTTXINTPROTECTION 1
#define LOSTINT_TIMEOUT (100/CLK_TCK)
/* Select RX interrupt enable bits. There are two models: (1) We interrupt
* when each character is received. Or, (2) we interrupt when either the Rx
* FIFO is half full, OR a timeout occurs with data in the RX FIFO. The
@ -229,12 +236,34 @@ struct up_dev_s
ubyte parity; /* 0=none, 1=odd, 2=even */
ubyte bits; /* Number of bits (7 or 8) */
boolean stopbits2; /* TRUE: Configure with 2 stop bits instead of 1 */
#ifdef CONFIG_UART_LOSTTXINTPROTECTION
boolean wdrunning; /* TRUE: The watchdog is running */
WDOG_ID wdog; /* Watchdog to catch missed UART interrupts */
#endif
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* Internal Helpers */
static inline uint16 up_serialin(struct up_dev_s *priv, int offset);
static inline void up_serialout(struct up_dev_s *priv, int offset, uint16 value);
static inline void up_disableuartint(struct up_dev_s *priv, uint16 *ier);
static inline void up_restoreuartint(struct up_dev_s *priv, uint16 ier);
#ifdef HAVE_CONSOLE
static inline void up_waittxnotfull(struct up_dev_s *priv);
#endif
#ifdef CONFIG_UART_LOSTTXINTPROTECTION
static void up_lostint(int argc, uint32 arg1, ...);
static int up_intinternal(struct uart_dev_s *dev);
#else
static inline int up_intinternal(struct uart_dev_s *dev);
#endif
/* Serial Driver Methods */
static int up_setup(struct uart_dev_s *dev);
static void up_shutdown(struct uart_dev_s *dev);
static int up_attach(struct uart_dev_s *dev);
@ -266,7 +295,7 @@ struct uart_ops_s g_uart_ops =
.send = up_send,
.txint = up_txint,
.txready = up_txready,
.txempty = up_txempty,
.txempty = up_txempty,
};
/* I/O buffers */
@ -479,6 +508,35 @@ static inline void up_waittxnotfull(struct up_dev_s *priv)
}
#endif
/****************************************************************************
* Name: up_lostint
*
* Description:
* Check for lost interrupts
*
****************************************************************************/
#ifdef CONFIG_UART_LOSTTXINTPROTECTION
static void up_lostint(int argc, uint32 arg1, ...)
{
struct uart_dev_s *dev = (struct uart_dev_s*)arg1;
struct up_dev_s *priv;
/* Check if we missed any interrupt conditions */
DEBUGASSERT(argc == 1 && dev && dev->priv);
(void)up_intinternal(dev);
/* Re-start a watchdog to catch more missed interrupts */
priv = (struct up_dev_s*)dev->priv;
if (priv->wdrunning)
{
wd_start(priv->wdog, LOSTINT_TIMEOUT, up_lostint, 1, (void*)dev);
}
}
#endif
/****************************************************************************
* Name: up_setup
*
@ -559,6 +617,11 @@ static int up_setup(struct uart_dev_s *dev)
/* Set up the IER */
priv->ier = up_serialin(priv, STR71X_UART_IER_OFFSET);
/* Set up a watchdog to catch missed UART interrupts */
#ifdef CONFIG_UART_LOSTTXINTPROTECTION
priv->wdog = wd_create();
#endif
#endif
return OK;
}
@ -576,6 +639,9 @@ static void up_shutdown(struct uart_dev_s *dev)
{
struct up_dev_s *priv = (struct up_dev_s*)dev->priv;
up_disableuartint(priv, NULL);
#ifdef CONFIG_UART_LOSTTXINTPROTECTION
wd_delete(priv->wdog);
#endif
}
/****************************************************************************
@ -646,45 +712,18 @@ static void up_detach(struct uart_dev_s *dev)
*
****************************************************************************/
static int up_interrupt(int irq, void *context)
#ifdef CONFIG_UART_LOSTTXINTPROTECTION
static int up_intinternal(struct uart_dev_s *dev)
#else
static inline int up_intinternal(struct uart_dev_s *dev)
#endif
{
struct uart_dev_s *dev = NULL;
struct up_dev_s *priv;
int passes;
boolean handled;
struct up_dev_s *priv;
int passes;
boolean handled;
#ifdef CONFIG_STR71X_UART0
if (g_uart0priv.irq == irq)
{
dev = &g_uart0port;
}
else
#endif
#ifdef CONFIG_STR71X_UART1
if (g_uart1priv.irq == irq)
{
dev = &g_uart1port;
}
else
#endif
#ifdef CONFIG_STR71X_UART2
if (g_uart2priv.irq == irq)
{
dev = &g_uart2port;
}
else
#endif
#ifdef CONFIG_STR71X_UART3
if (g_uart3priv.irq == irq)
{
dev = &g_uart3port;
}
else
#endif
{
PANIC(OSERR_INTERNAL);
}
priv = (struct up_dev_s*)dev->priv;
DEBUGASSERT(priv && dev);
/* Loop until there are no characters to be transferred or,
* until we have been looping for a long time.
@ -724,6 +763,44 @@ static int up_interrupt(int irq, void *context)
return OK;
}
static int up_interrupt(int irq, void *context)
{
struct uart_dev_s *dev = NULL;
#ifdef CONFIG_STR71X_UART0
if (g_uart0priv.irq == irq)
{
dev = &g_uart0port;
}
else
#endif
#ifdef CONFIG_STR71X_UART1
if (g_uart1priv.irq == irq)
{
dev = &g_uart1port;
}
else
#endif
#ifdef CONFIG_STR71X_UART2
if (g_uart2priv.irq == irq)
{
dev = &g_uart2port;
}
else
#endif
#ifdef CONFIG_STR71X_UART3
if (g_uart3priv.irq == irq)
{
dev = &g_uart3port;
}
else
#endif
{
PANIC(OSERR_INTERNAL);
}
return up_intinternal(dev);
}
/****************************************************************************
* Name: up_ioctl
*
@ -848,19 +925,73 @@ static void up_send(struct uart_dev_s *dev, int ch)
static void up_txint(struct uart_dev_s *dev, boolean enable)
{
#ifdef CONFIG_UART_LOSTTXINTPROTECTION
struct up_dev_s *priv = (struct up_dev_s*)dev->priv;
irqstate_t flags;
flags = irqsave();
if (enable)
{
/* Set to receive an interrupt when the TX fifo is half emptied */
#ifndef CONFIG_SUPPRESS_SERIAL_INTS
priv->ier |= STR71X_UARTSR_THE;
up_serialout(priv, STR71X_UART_IER_OFFSET, priv->ier);
/* Start a watchdog to catch missed UART Tx interrupts (Need to do
* this before calling uart_xmitchars() because that function
* could recurse and disable Tx interrupts before returning.
*/
wd_start(priv->wdog, LOSTINT_TIMEOUT, up_lostint, 1, (void*)dev);
priv->wdrunning = TRUE;
/* The serial driver wants an interrupt here, but will not get get
* one unless we "prime the pump."
*/
uart_xmitchars(dev);
#endif
}
else
{
/* Stop the watchdog if it is running */
if (priv->wdrunning)
{
wd_cancel(priv->wdog);
priv->wdrunning = FALSE;
}
/* Disable the TX interrupt */
priv->ier &= ~STR71X_UARTSR_THE;
up_serialout(priv, STR71X_UART_IER_OFFSET, priv->ier);
}
irqrestore(flags);
#else /* CONFIG_UART_LOSTTXINTPROTECTION */
struct up_dev_s *priv = (struct up_dev_s*)dev->priv;
if (enable)
{
/* Set to receive an interrupt when the TX fifo is half emptied */
#ifndef CONFIG_SUPPRESS_SERIAL_INTS
priv->ier |= STR71X_UARTSR_THE;
#endif
}
else
{
/* Disable the TX interrupt */
priv->ier &= ~STR71X_UARTSR_THE;
}
up_serialout(priv, STR71X_UART_IER_OFFSET, priv->ier);
#endif /* CONFIG_UART_LOSTTXINTPROTECTION */
}
/****************************************************************************

View File

@ -1,7 +1,7 @@
/****************************************************************************
* wd_cancel.c
*
* Copyright (C) 2007, 2008 Gregory Nutt. All rights reserved.
* Copyright (C) 2007-2009 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <spudmonkey@racsa.co.cr>
*
* Redistribution and use in source and binary forms, with or without
@ -98,9 +98,9 @@ STATUS wd_cancel (WDOG_ID wdid)
saved_state = irqsave();
/* Make sure that the the watchdog is still active */
/* Make sure that the watchdog is initialed (non-NULL) and is still active */
if (wdid->active)
if (wdid && wdid->active)
{
/* Search the g_wdactivelist for the target FCB. We can't use sq_rem
* to do this because there are additional operations that need to be