This is the mail archive of the ecos-discuss@sources.redhat.com mailing list for the eCos project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Atmel/AT91/ARM SERIAL PORT DRIVER REWRITTEN WITH HARDWARE BUFFERING


There are several problems with the current Atmel derial driver.
I have fixed most of them with this almost entirely rewritten
driver.

Lets first explain the problems:

1 - the serial driver does no hardware buffering even though
  Atmel PDC is cabable of it. So it drops characters OFTEN.

2 - even with hardware buffering you need to cope with an
   ENDRX interupt on a buffer full. But eCos cannot call
   the ISR function fast enough so it drops a couple
    of characters at the end of each buffer.

The solution is to use the PDC in both directions,
disable RXRDY/TXRDY interrupts entirely, and insert
some REALLY fast flip buffer routine inside eCos's
main interrupt handler.

Note that the code below assumes our board. Simply replace
SETH with AT91 and seth with at91 to get a at91 driver.

Our first order of business is to optimise the
eCos irq handler. Below is an optmised version
with a flip buffer handler.

The rest of the sources are attached.

I have made the buffer really large: SETH_UART_BUF_SIZE = 1024
Probably a buffer of 128 is more than sufficient.
I have only tested uart0. but uart1 is symmetrically coded,
so it should work fine.

here is the new irq routine:
----------------

FLIP_BUFFER_DEF(SETH_UART_BUF_SIZE);
static struct flip_buffer *uart0, *uart1;

void flip_set_uart0 (struct flip_buffer *u)
{
    uart0 = u;
}

void flip_set_uart1 (struct flip_buffer *u)
{
    uart1 = u;
}

//
// This routine is called to respond to a hardware interrupt (IRQ).  It
// should interrogate the hardware and return the IRQ vector number.
int hal_IRQ_handler (void)
{
    cyg_uint32 irq_num = 0;
    cyg_uint32 ipr, imr, count, p;

    HAL_READ_UINT32 (SETH_AIC + SETH_AIC_IPR, ipr);
    HAL_READ_UINT32 (SETH_AIC + SETH_AIC_IMR, imr);
    ipr &= imr;
    if (ipr & (1 << 0))
	goto done;
    irq_num = 1;
    if (ipr & (1 << 1))
	goto done;
    irq_num = CYGNUM_HAL_INTERRUPT_USART0;
    if ((ipr & (1 << CYGNUM_HAL_INTERRUPT_USART0))) {
	HAL_READ_UINT32 (SETH_USART0 + SETH_US_CSR, ipr);
	HAL_READ_UINT32 (SETH_USART0 + SETH_US_IMR, imr);
	if ((ipr & imr & (SETH_US_IER_ENDRX | SETH_US_IER_TIMEOUT)) && uart0) {
	    HAL_READ_UINT32 (SETH_USART0 + SETH_US_RPR, p);
	    count = p - (cyg_uint32) uart0->buf[uart0->buf_which];
	    if (count > 0) {	// PDC has done work
		/* record the location of the bytes read while we were away */
		uart0->buf_p = uart0->buf[uart0->buf_which];
		uart0->buf_count = count;
		/* flip the buffer */
		uart0->buf_which = 1 - uart0->buf_which;
		HAL_WRITE_UINT32 (SETH_USART0 + SETH_US_RPR, (cyg_uint32) uart0->buf[uart0->buf_which]);
		HAL_WRITE_UINT32 (SETH_USART0 + SETH_US_RCR, (cyg_uint32) SETH_UART_BUF_SIZE);
	    }
	    HAL_WRITE_UINT32 (SETH_USART0 + SETH_US_RTOR, RECV_UART_CHAR_TIMEOUT);
	    HAL_WRITE_UINT32 (SETH_USART0 + SETH_US_CR, SETH_US_CR_STTTO);
	}
	goto done;
    }
    irq_num = CYGNUM_HAL_INTERRUPT_USART1;
    if ((ipr & (1 << CYGNUM_HAL_INTERRUPT_USART1))) {
	HAL_READ_UINT32 (SETH_USART1 + SETH_US_CSR, ipr);
	HAL_READ_UINT32 (SETH_USART1 + SETH_US_IMR, imr);
	if ((ipr & imr & (SETH_US_IER_ENDRX | SETH_US_IER_TIMEOUT)) && uart1) {
	    HAL_READ_UINT32 (SETH_USART1 + SETH_US_RPR, p);
	    count = p - (cyg_uint32) uart1->buf[uart1->buf_which];
	    if (count > 0) {	// PDC has done work
		/* record the location of the bytes read while we were away */
		uart1->buf_p = uart1->buf[uart1->buf_which];
		uart1->buf_count = count;
		/* flip the buffer */
		uart1->buf_which = 1 - uart1->buf_which;
		HAL_WRITE_UINT32 (SETH_USART1 + SETH_US_RPR, (cyg_uint32) uart1->buf[uart1->buf_which]);
		HAL_WRITE_UINT32 (SETH_USART1 + SETH_US_RCR, (cyg_uint32) SETH_UART_BUF_SIZE);
	    }
	    HAL_WRITE_UINT32 (SETH_USART1 + SETH_US_RTOR, RECV_UART_CHAR_TIMEOUT);
	    HAL_WRITE_UINT32 (SETH_USART1 + SETH_US_CR, SETH_US_CR_STTTO);
	}
	goto done;
    }
    irq_num = CYGNUM_HAL_INTERRUPT_TIMER0;
    if (ipr & (1 << CYGNUM_HAL_INTERRUPT_TIMER0))
	goto done;
    irq_num = CYGNUM_HAL_INTERRUPT_TIMER1;
    if (ipr & (1 << CYGNUM_HAL_INTERRUPT_TIMER1))
	goto done;
    irq_num = CYGNUM_HAL_INTERRUPT_TIMER2;
    if (ipr & (1 << CYGNUM_HAL_INTERRUPT_TIMER2))
	goto done;
    irq_num = CYGNUM_HAL_INTERRUPT_WATCHDOG;
    if (ipr & (1 << CYGNUM_HAL_INTERRUPT_WATCHDOG))
	goto done;
    irq_num = CYGNUM_HAL_INTERRUPT_PIO;
    if (ipr & (1 << CYGNUM_HAL_INTERRUPT_PIO))
	goto done;
    irq_num = CYGNUM_HAL_INTERRUPT_EXT0;
    if (ipr & (1 << CYGNUM_HAL_INTERRUPT_EXT0))
	goto done;
    irq_num = CYGNUM_HAL_INTERRUPT_EXT1;
    if (ipr & (1 << CYGNUM_HAL_INTERRUPT_EXT1))
	goto done;
    irq_num = CYGNUM_HAL_INTERRUPT_EXT2;
    if (ipr & (1 << CYGNUM_HAL_INTERRUPT_EXT2))
	goto done;
    irq_num = CYGNUM_HAL_ISR_MAX + 1;
  done:
    return irq_num;
}




---------------------------------------------
This message was sent using World Mail.
http://www.worldonline.co.za

Attachment: serial.tar.gz
Description: Binary data


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]