? fifofix.txt Index: ChangeLog =================================================================== RCS file: /cvs/ecos/ecos/packages/devs/serial/arm/at91/current/ChangeLog,v retrieving revision 1.11 diff -u -w -r1.11 ChangeLog --- ChangeLog 26 Jan 2004 08:38:22 -0000 1.11 +++ ChangeLog 25 Oct 2004 12:37:38 -0000 @@ -1,3 +1,10 @@ +2004-10-25 Oyvind Harboe + + * changed how hardware fifos are handled + added >2 double buffering. This is + required to make sure that data is not lost when lots of interrupts are happening + in the system. This applies even at low speeds(38400). Typically this can be seen + when both serial ports are used simultaneously. + 2004-01-26 Andrew Lunn * src/at91_serial.c (at91_serial_getc_polled) Index: src/at91_serial.c =================================================================== RCS file: /cvs/ecos/ecos/packages/devs/serial/arm/at91/current/src/at91_serial.c,v retrieving revision 1.9 diff -u -w -r1.9 at91_serial.c --- src/at91_serial.c 26 Jan 2004 08:38:24 -0000 1.9 +++ src/at91_serial.c 25 Oct 2004 12:37:39 -0000 @@ -80,6 +80,8 @@ #define SIFLG_XMIT_BUSY 0x02 #define SIFLG_XMIT_CONTINUE 0x04 +#define BUFNUM 6 + typedef struct at91_serial_info { CYG_ADDRWORD base; CYG_WORD int_num; @@ -87,10 +89,12 @@ int transmit_size; cyg_interrupt serial_interrupt; cyg_handle_t serial_interrupt_handle; - cyg_uint8 *rcv_buffer[2]; + cyg_uint8 rcv_buffer[BUFNUM][CYGNUM_IO_SERIAL_ARM_AT91_SERIAL1_RCV_CHUNK_SIZE + RCVBUF_EXTRA]; cyg_uint16 rcv_chunk_size; - cyg_uint8 curbuf; + cyg_int32 curbufwrite; + cyg_int32 curbufread; cyg_uint8 flags; + cyg_uint8 *end[BUFNUM]; } at91_serial_info; static bool at91_serial_init(struct cyg_devtab_entry *tab); @@ -138,13 +142,10 @@ #endif #ifdef CYGPKG_IO_SERIAL_ARM_AT91_SERIAL0 -static cyg_uint8 at91_serial_rcv_buffer_0 - [2][CYGNUM_IO_SERIAL_ARM_AT91_SERIAL0_RCV_CHUNK_SIZE + RCVBUF_EXTRA]; static at91_serial_info at91_serial_info0 = { base : (CYG_ADDRWORD) AT91_USART0, int_num : CYGNUM_HAL_INTERRUPT_USART0, rcv_chunk_size : CYGNUM_IO_SERIAL_ARM_AT91_SERIAL0_RCV_CHUNK_SIZE, - rcv_buffer : {at91_serial_rcv_buffer_0[0], at91_serial_rcv_buffer_0[1]} }; #if CYGNUM_IO_SERIAL_ARM_AT91_SERIAL0_BUFSIZE > 0 @@ -185,13 +186,10 @@ #endif // CYGPKG_IO_SERIAL_ARM_AT91_SERIAL1 #ifdef CYGPKG_IO_SERIAL_ARM_AT91_SERIAL1 -static cyg_uint8 at91_serial_rcv_buffer_1 - [2][CYGNUM_IO_SERIAL_ARM_AT91_SERIAL1_RCV_CHUNK_SIZE + RCVBUF_EXTRA]; static at91_serial_info at91_serial_info1 = { base : (CYG_ADDRWORD) AT91_USART1, int_num : CYGNUM_HAL_INTERRUPT_USART1, rcv_chunk_size : CYGNUM_IO_SERIAL_ARM_AT91_SERIAL1_RCV_CHUNK_SIZE, - rcv_buffer : {at91_serial_rcv_buffer_1[0], at91_serial_rcv_buffer_1[1]} }; #if CYGNUM_IO_SERIAL_ARM_AT91_SERIAL1_BUFSIZE > 0 @@ -260,7 +258,13 @@ HAL_WRITE_UINT32(base + AT91_US_IDR, 0xFFFFFFFF); // Start receiver - at91_chan->curbuf = 0; + at91_chan->curbufwrite = 0; + at91_chan->curbufread = 0; + int i; + for (i=0; iend[i] = NULL; + } HAL_WRITE_UINT32(base + AT91_US_RPR, (CYG_ADDRESS) at91_chan->rcv_buffer[0]); HAL_WRITE_UINT32(base + AT91_US_RTO, RCV_TIMEOUT); HAL_WRITE_UINT32(base + AT91_US_IER, AT91_US_IER_ENDRX | AT91_US_IER_TIMEOUT); @@ -290,7 +294,13 @@ #ifdef CYGDBG_IO_INIT diag_printf("AT91 SERIAL init - dev: %x.%d\n", at91_chan->base, at91_chan->int_num); #endif - at91_chan->curbuf = 0; + int i; + for (i=0; iend[i] = NULL; + } + at91_chan->curbufwrite = 0; + at91_chan->curbufread = 0; at91_chan->flags = SIFLG_NONE; at91_chan->stat = 0; (chan->callbacks->serial_init)(chan); // Really only required for interrupt driven devices @@ -461,17 +471,32 @@ HAL_READ_UINT32(base + AT91_US_IMR, mask); stat &= mask; - if (stat & (AT91_US_IER_ENDRX | AT91_US_IER_TIMEOUT)) { - cyg_uint32 x; - HAL_WRITE_UINT32(base + AT91_US_IDR, AT91_US_IER_ENDRX | AT91_US_IER_TIMEOUT); + if (stat & AT91_US_IER_TIMEOUT) { + /* + * the FIFO is still running, we have to stop feeding it + * and wait for it to become idle + */ + HAL_WRITE_UINT32(base + AT91_US_IDR, AT91_US_IER_TIMEOUT); HAL_WRITE_UINT32(base + AT91_US_RCR, 0); HAL_WRITE_UINT32(base + AT91_US_RTO, 0); - HAL_READ_UINT32(base + AT91_US_RPR, x); - HAL_WRITE_UINT32( - base + AT91_US_RCR, - (CYG_ADDRESS) at91_chan->rcv_buffer[at91_chan->curbuf] - + at91_chan->rcv_chunk_size + RCVBUF_EXTRA - x - ); + } + + if (stat & AT91_US_IER_ENDRX) { + /* the FIFO is currently stopped, restart it. This needs to happen + * at the highest interrupt priority since the AT91 serial hardware has + * a very tight window for doing this. + */ + const cyg_uint32 cb = at91_chan->curbufwrite, nb = (cb+1)%BUFNUM; + at91_chan->curbufwrite = nb; + + cyg_uint8 *end; + HAL_READ_UINT32(base + AT91_US_RPR, (CYG_ADDRESS) end); + CYG_ASSERT(at91_chan->end[cb]==NULL, ("serial receive DSR was not able to process quickly enough\n")); + at91_chan->end[cb]=end; + + HAL_WRITE_UINT32(base + AT91_US_CR, AT91_US_CR_RSTATUS | AT91_US_CR_STTTO); + HAL_WRITE_UINT32(base + AT91_US_RPR, (CYG_ADDRESS) at91_chan->rcv_buffer[nb]); + HAL_WRITE_UINT32(base + AT91_US_RCR, at91_chan->rcv_chunk_size); } if (stat & (AT91_US_IER_TxRDY | AT91_US_IER_ENDTX)) @@ -479,6 +504,7 @@ at91_chan->stat |= stat; cyg_drv_interrupt_acknowledge(vector); + return CYG_ISR_CALL_DSR; } @@ -496,21 +522,19 @@ at91_chan->stat = 0; cyg_drv_interrupt_unmask(vector); - if (stat & (AT91_US_IER_ENDRX | AT91_US_IER_TIMEOUT)) { - const cyg_uint8 cb = at91_chan->curbuf, nb = cb ^ 0x01; - const cyg_uint8 * p = at91_chan->rcv_buffer[cb], * end; - - at91_chan->curbuf = nb; - HAL_WRITE_UINT32(base + AT91_US_RCR, 0); - HAL_READ_UINT32(base + AT91_US_RPR, (CYG_ADDRESS) end); - HAL_WRITE_UINT32(base + AT91_US_RTO, RCV_TIMEOUT); - HAL_WRITE_UINT32(base + AT91_US_CR, AT91_US_CR_RSTATUS | AT91_US_CR_STTTO); - HAL_WRITE_UINT32(base + AT91_US_RPR, (CYG_ADDRESS) at91_chan->rcv_buffer[nb]); - HAL_WRITE_UINT32(base + AT91_US_RCR, at91_chan->rcv_chunk_size); - HAL_WRITE_UINT32( - base + AT91_US_IER, - AT91_US_IER_ENDRX | AT91_US_IER_TIMEOUT - ); + if (stat & AT91_US_IER_ENDRX) { + while (at91_chan->curbufread!=at91_chan->curbufwrite) + { + cyg_uint8 *end; + const cyg_uint8 *p; + const cyg_uint32 cb = at91_chan->curbufread; + at91_chan->curbufread=(at91_chan->curbufread+1)%BUFNUM; + + CYG_ASSERT((at91_chan->end[cb]==NULL),("serial DSR received interrupt without data\n")); + + end=at91_chan->end[cb]; + at91_chan->end[cb]=NULL; + p=at91_chan->rcv_buffer[cb]; while (p < end) { rcv_req_reply_t res; @@ -537,12 +561,16 @@ default: // Buffer full or unknown error, can't do anything about it // Discard data - CYG_FAIL("Serial receiver buffer overflow"); + // CYG_FAIL("Serial receiver buffer overflow"); p = end; break; } } } + // now we want timeout interrupts again. + HAL_WRITE_UINT32(base + AT91_US_RTO, RCV_TIMEOUT); + HAL_WRITE_UINT32(base + AT91_US_IER, AT91_US_IER_TIMEOUT); + } if (stat & AT91_US_IER_TxRDY) { at91_chan->flags &= ~SIFLG_XMIT_BUSY;