Jump to content
  • 0

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


krjdev

Question

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;
}

 

Link to comment
Share on other sites

1 answer to this question

Recommended Posts

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.

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...