• 0
krjdev

[Zybo-Z7] Bare-Metal - UART interface - Undefined behavior or understanding the register XUARTPS_ISR

Question

Posted (edited)

Hi,

 

I have noticed an issue in a bare-metal application when polling the register XUARTPS_ISR (Channel Interrupt Status Register).

No issues when I use XUART_SR (Channel Status Register) instead.

 

The issue:

When I poll XUART_ISR for TX_FULL and RX_EMPTY the UART interface becomes an unstable state. No sending and receiving after a few bytes is possible.

Has somebody noticed the same issue or can somebody explain whats happening?

 

Edit:

 

My current code for the UART interface (Here I use XUARTPS_SR instead. But when I poll XUARTPS_ISR (0x14) it doesn't work.):

#include <string.h>

#include <dev/io.h>

#define UART0_BASE          0xE0000000
#define UART1_BASE          0xE0001000

#define UART_CR_OFFSET      0x0
    
    #define UART_CR_RXRST   0
    #define UART_CR_TXRST   1
    #define UART_CR_RXEN    2
    #define UART_CR_RXDIS   3
    #define UART_CR_TXEN    4
    #define UART_CR_TXDIS   5

#define UART_MR_OFFSET      0x4
#define UART_IER_OFFSET     0x8
#define UART_BGEN_OFFSET    0x18
#define UART_SR_OFFSET      0x2C

    #define UART_SR_RXEMPT  1
    #define UART_SR_RXFULL  2
    #define UART_SR_TXEMPT  3
    #define UART_SR_TXFULL  4

#define UART_FIFO_OFFSET    0x30

#define UART_BDIV_OFFSET    0x34

struct uart_reg {
    unsigned int cr;
    unsigned int mr;
    unsigned int ier;
    unsigned int bgen;
    unsigned int sr;
    unsigned int fifo;
    unsigned int bdiv;
};

static struct uart_reg regs;

void uart_init(void)
{
    unsigned int tmp;
    
    regs.cr = (UART1_BASE + UART_CR_OFFSET);
    regs.mr = (UART1_BASE + UART_MR_OFFSET);
    regs.ier = (UART1_BASE + UART_IER_OFFSET);
    regs.bgen = (UART1_BASE + UART_BGEN_OFFSET);
    regs.sr = (UART1_BASE + UART_SR_OFFSET);
    regs.fifo = (UART1_BASE + UART_FIFO_OFFSET);
    regs.bdiv = (UART1_BASE + UART_BDIV_OFFSET);
    
    /* Check if RX/TX is enabled */
    tmp = io_rd32(regs.cr);
    
    /* Skip init if RX/TX enabled */
    if (tmp & ((1 << UART_CR_TXEN) | (1 << UART_CR_RXEN)))
        return;
    
    /* Reset RX/TX paths */
    tmp = io_rd32(regs.cr);
    tmp |= ((1 << UART_CR_TXRST) | (1 << UART_CR_RXRST));
    io_wr32(regs.cr, tmp);
    
    do
        tmp = io_rd32(regs.cr);
    while (tmp & ((1 << UART_CR_TXRST) | (1 << UART_CR_RXRST)));
    
    /* Set baudrate to 115200; UART Reference Clock: 100MHz */
    io_wr32(regs.bgen, 124);
    io_wr32(regs.bdiv, 6);
    
    /* Set mode: 8 Databit, 1 Stopbit, No Parity */
    io_wr32(regs.mr, 0x20);
    
    /* Enable RX/TX */
    tmp = io_rd32(regs.cr);
    tmp &= ~((1 << UART_CR_TXEN) | (1 << UART_CR_RXEN));
    io_wr32(regs.cr, tmp);
}

int uart_send(unsigned char *buf, int len)
{
    int i;
    unsigned int tmp;
    
    for (i = 0; i < len; i++) {
        do
            tmp = io_rd32(regs.sr);
        while (tmp & (1 << UART_SR_TXFULL));
        
        io_wr08(regs.fifo, buf[i]);
    }
    
    return len;
}

int uart_recv(unsigned char *buf, int len)
{
    int i;
    int ret = 0;
    unsigned int tmp;
    
    tmp = io_rd32(regs.sr);
    
    if (tmp & (1 << UART_SR_RXEMPT))
        return 0;
    
    for (i = 0; i < len; i++) {
        buf[i] = io_rd08(regs.fifo);
        ret++;
        tmp = io_rd32(regs.sr);
    
        if (tmp & (1 << UART_SR_RXEMPT))
            return ret;
    }
    
    return ret;
}

 

Edited by krjdev
add missing current code;

Share this post


Link to post
Share on other sites

1 answer to this question

Recommended Posts

  • 0
Posted (edited)

Haven't seen low-level code in a while.

Check the register reference in https://www.xilinx.com/support/documentation/user_guides/ug585-Zynq-7000-TRM.pdf. Page 1773 deals with these registers.

The difference seems to be that SR is a read-only register reflecting real-time hardware status, while ISR is read and write-to-clear. ISR is used by the interrupt handler to handle the interrupt event and clear the status bits at the end then wait for it to be set again. If not cleared and the byte is consumed/produced, an erroneous interrupt will trigger again with no data to read/write.

Xilinx's drivers are a good reference on how to use their peripherals programatically.

Edited by elodg

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now