Forum Discussion

Altera_Forum's avatar
Altera_Forum
Icon for Honored Contributor rankHonored Contributor
12 years ago

ISR driven UART is missing characters (even at 9600bps... same prob at 230400)

Any ideas on what I'm missing here that is causing me to miss characters?

I'm interfacing to a device that shoots out RS232 packets (up to 200 per second if the bps can handle itI can select the BPS, but it is always N-8-1). Right now, I have the device running (and the UART) at 9600bps and locked to 5 packets per second... although, this problem happens at 115200, 230400, and is terrible at 460800bps(which is where I need to get).

The UART seems to be missing characters, especially towards the end of the 'packets' which are around 100bytes in length. towards the front of the packet I seem to get everything and towards the 'rear' it starts to miss them... the 'ISR' itself generally takes 500ns to execute, but that doesn't include the call latency... but I can't see that being a problem at 9600bps... Also the ring-buffer is 4k in size. I have set a breakpoint so that I can debug after it gets 1024 characters and the ring-buffer is missing them... so it can't be a wrapping/overflow problem.... grrrr.

Here's how I'm setting up my code in main to test this problem:

alt_irq_init (ALT_IRQ_BASE);

alt_sys_init();

unsigned char ch;

unsigned int baudrate=9600;

UsartInit(baudrate);

UsartInit_2(baudrate);

//register the ISR handler for UART 0)

alt_ic_isr_register(UART0_IRQ_INTERRUPT_CONTROLLER_ID,

UART0_IRQ,

IsrUsart,0,0);

//register the ISR handler for UART 1

alt_ic_isr_register(UART1_IRQ_INTERRUPT_CONTROLLER_ID,

UART1_IRQ,

IsrUsart_2,0,0);

//enable Interrupts for UART 0

alt_ic_irq_enable(UART0_IRQ_INTERRUPT_CONTROLLER_ID,

UART0_IRQ);

//enable Interrupts for UART 1

alt_ic_irq_enable(UART1_IRQ_INTERRUPT_CONTROLLER_ID,

UART1_IRQ);

There there's the big 'while(1)'in main():

while (1)

{ //update heartbeat on JP5 header

jp5tracker ^= 0x01;//D0 on L.A.

IOWR_ALTERA_AVALON_PIO_DATA(JP5_BASE, jp5tracker);

while(!UsartIsEmpty_2())

{ //toggle LEDG1 when characters are processed from VN100 plugged into the HSMC-ICB

greenLEDtracker ^= 0x02;

IOWR_ALTERA_AVALON_PIO_DATA ( GLED_BASE , greenLEDtracker );

ch=GetUsart_2();

jp5tracker ^= 0x02;//D1 on L.A.

IOWR_ALTERA_AVALON_PIO_DATA(JP5_BASE, jp5tracker);

i++;//counter to set a breakpoint with

}

here's the ISR handler and some helpers for the ring buffer.

void UsartInit_2(unsigned int BaudRate_2)

{

unsigned int divisor_2;

divisor_2 = (ALT_CPU_FREQ/BaudRate_2)+1;

IOWR_ALTERA_AVALON_UART_DIVISOR(UART1_BASE, divisor_2);

IOWR_ALTERA_AVALON_UART_CONTROL(UART1_BASE, ALTERA_AVALON_UART_CONTROL_RRDY_MSK);

}

void IsrUsart_2(void* context_2, unsigned int id_2)

{

IOWR_ALTERA_AVALON_PIO_DATA(JP5_BASE, 0xFF);//inserted to time/track this routine

//grab data byte and save it

USART_RX_buffer_2[USART_RX_hd_2++] = IORD_ALTERA_AVALON_UART_RXDATA(UART1_BASE);

//move/wrap buffer pointer

if (USART_RX_buffer_2 == '*')

z_2==1;

if (USART_RX_hd_2 == USART_BUFFER_SIZE_2) USART_RX_hd_2 = 0;

USART_RX_full_2 = (USART_RX_hd_2==USART_RX_tl_2);

IOWR_ALTERA_AVALON_PIO_DATA(JP5_BASE, 0x00);//inserted to time/track this routine

}

unsigned char UsartIsEmpty_2()

{

return ((!USART_RX_full_2) && (USART_RX_hd_2==USART_RX_tl_2));

}

//this grabs a single character from the RX circular buffer.

unsigned char GetUsart_2(void)

{

unsigned char rxChar_2;

//should never call this w/o checkign status, but it may happen. this keeps

//the ring buffer sane during that sort of abuse.

if (((!USART_RX_full_2) && (USART_RX_hd_2==USART_RX_tl_2)))

return 0x00;

else

{

rxChar_2=USART_RX_buffer_2[USART_RX_tl_2++];

if (USART_RX_tl_2 == (USART_BUFFER_SIZE_2))

USART_RX_tl_2=0;

USART_RX_full_2=0;

return rxChar_2;

}

}

Anyway... if I write some debug code on a PC to send data... as long as I insert some 'sleeps' in it between characters... never misses a character... Also, I can watch the ISR's debug 'flip' of JP5 and the ISR does get called, and the circular buffer does get populated... it is just missing characters. I have verified (with a PC's serial port) that the packets are being sent properly and contain all the data.

Here's what I'm using:

DE2-115 development board(cyclone IV) with HSMC-ICB (Altera's INK)

Quartus13.0 SP1 with EDS, the UART is:

UART (RS-232 Serial Port)

Name altera_avalon_uart

Version 13.0.1.99.2

Some BSP settings:

enable_small_c_library-'checked'.... 'nocheck' for enable_gprof, enable_reduced_device_drivers, or enable_sim_optimize

'none' for sys_clk_timer, and timestamp_timer

13 Replies

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    I'm just starting with these tools, but that does make sense... it is behaving like a stall/flush. I was trying to get the baseline running before I started optimizations to add more features.

    I'm wondering if I need to swap the processor to a Nios II/S and lower the amout of instruction cache... refilling that would slow ISR performance too, but the II/F runs really fast in a straight line... It takes about 30 minutes to Generate/Recompile and then I can run my timing tests, so I need to implement some other things and then I can come back to this one and optimize it.
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Hi David,

    i dont understand your problem, but your code for uart was the first, which works me.

    Thank you. (i have had problem in Quartus 12, NIOS2 EDS 12 that the# include <sys/alt_irq.h> didnt know these commands

    alt_irq_register(UART1_IRQ,&context_uart1,IsrUart1 ); // install UART1 ISR

    alt_irq_enable (UART1_IRQ);

    When i use your init_uart and handle routine, it works:

    // obsluha preruseni od uartu1

    void IsrUart1(void* context, unsigned long id)

    {

    int ch;

    ch = IORD_ALTERA_AVALON_UART_STATUS(UART_1_BASE);

    // printf("Jsem v preruseni=%X\n",ch);

    // pokud prisel znak, precti jej

    if (ch & ALTERA_AVALON_UART_STATUS_RRDY_MSK)

    { ch = IORD_ALTERA_AVALON_UART_RXDATA(UART_1_BASE);

    // printf("Ctu znak=%X\n",ch);

    IOWR_ALTERA_AVALON_UART_STATUS(UART_1_BASE, 0);

    if (ch==GPS_PREFIX) rx_start=1;

    if (rx_start==1)

    { rx_buf[rx_cnt]= ch;

    if (rx_cnt<RX_BUF) rx_cnt++;

    if (rx_cnt==RX_BUF) { rx_flag=1; rx_start=0; }

    }

    }

    }

    // inicializace preruseni od uartu1

    void init_uart1()

    {

    alt_irq_init (ALT_IRQ_BASE);

    // alt_sys_init(); //

    // nastav 9600Bd pro NIOS2 na 80MHz

    IOWR_ALTERA_AVALON_UART_DIVISOR(UART_1_BASE, 8334);

    IOWR_ALTERA_AVALON_UART_CONTROL(UART_1_BASE, ALTERA_AVALON_UART_CONTROL_RRDY_MSK);

    alt_ic_isr_register(UART_1_IRQ_INTERRUPT_CONTROLLER_ID,UART_1_IRQ,IsrUart1,0,0);

    // povol preruseni od uartu1

    alt_ic_irq_enable(UART_1_IRQ_INTERRUPT_CONTROLLER_ID,UART_1_IRQ);

    }

    Thanks.

    Jan, Czech Republic
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Glad it could help you. And just like dsl said.... My problem was all in how the ISR was handled, the code was fine--I had effectively shot myself in the foot with the NIOSII configuraiton (lots of cache/etc..) That's what makes problems like I had so complicated to fix... there was nothing wrong with the 'C'... it was in how I had configured the NIOSII and the UART... enlarging the buffer and leaving the CPU alone, I was able to run all the way up to 250Kbps (which is the limit of the DE2-115 level converter--e.g. as fast as my hardware could support.. the FPGA/NIOSII/UART could have gone faster.)