Jetson TK1 UART issues

I’m coding bare-metal and am having issues communicating with the UART port (UART4, attached to serial). I can get everything over serial, without an issue. If I code something along the lines of (UART4 is a volatile ptr to the base addr - 0x70006300):

void bwputs(char *s) {
    while(*s) {
        *UART4 = *s;
        s++;
    }
}

and call that:

bwputs("Hello, World!");

It works fine (though it truncates over 15 chars, I’m assuming because the buffer is full?).

As soon as I try to get some FIFO going though (adapted from osdev.org wiki):

void init_serial() {
    *(UART4 + 1) = 0x00;    // Disable all interrupts
    *(UART4 + 3) = 0x80;    // Enable DLAB (set baud rate divisor)
    *(UART4 + 0) = 0x03;    // Set divisor to 3 (lo byte) 38400 baud
    *(UART4 + 1) = 0x00;    //                  (hi byte)
    *(UART4 + 3) = 0x03;    // 8 bits, no parity, one stop bit
    *(UART4 + 2) = 0xC7;    // Enable FIFO, clear them, with 14-byte threshold
    *(UART4 + 4) = 0x0B;    // IRQs enabled, RTS/DSR set
}

int serial_received() {
   return (*(UART4 + 5) & 1);
}
 
char read_serial() {
   while (serial_received() == 0);
 
   return *UART4;
}

int is_transmit_empty() {
   return (*(UART4 + 5) & 0x20);
}
 
void write_serial(char a) {
   while (is_transmit_empty() == 0);
 
   *UART4 = a;
}

void bwputs(char *s) {
    while(*s) {
        write_serial(*s);
        s++;
    }
}

called as:

init_serial();
bwputs("Hello, World!");

It won’t transmit anything.

There’s a very good chance I’m missing something, since I’m new to this…but I’ve been pulling my hair out over it. Any advice would be awesome, thanks!

Usually when Txing/Rxing with a UART - you need to check the status of the UART before doing the next character.

Unfortunately the TegraK1 TRM is very poor when it comes to describing how to use each peripheral.
The TRM says the UART is compatible with 16450 and 16550. Have a look for “technical notes” or application notes or programming guides for these.

A quick scan of the UART section of the TRM - the UART_LSR_0 (UART Line Status Register) has TX_FIFO_FULL and TMTY for checking a Tx character is done.

Thanks for the reply, GE_Chen!

I’ve been referring to documentation specifically on 16450-compatible serial, per the TK1 TRM, as well as the aforementioned osdev wiki and FreeBSD’s documentation (https://www.freebsd.org/doc/en/articles/serial-uart/).

I think I’m doing that properly in “is_transmit_empty()”, but if that’s not the case, wouldn’t I still be able to receive the first CH before THRE + line-busy (LSR bits 6 and 7) triggered, locking it in the infinity loop?

I’ll definitely give this a try when I get home. I haven’t seen this mentioned in any of the general 16450 docs (they all say to refer to TEMT and THRE on the LSR), but it can’t hurt!

Sorry, I have seen your modified version of bwputs() which calls is_transmit_empty()

“It won’t transmit anything.”
Does the program lock up as by any chance?

My guess is that it gets stuck in an infinite loop waiting for the UART to become free.

Build in a timeout mechanism.

My guess is also that the init routine is not giving enough time for each register write. For each write, it should change or set up some condition in the UART. The code should check this has happened before moving on. If it fails on any of the set ups - it should return a failure.

FYI
In bwputs() - try not to do it by calling is_transmit_empty(). Every time the function makes a function call, it has to push stuff on the stack and then on return unwind the stack. For such a small simple loop this is an unnecessary overhead.

I’m curious about the 16450 mode…nowadays (and for a very long time) I think 16450 has been considered “obsolete” (so far as hardware is concerned…software doesn’t really care) and 16550 is preferred (very little dedicated serial UART hardware these days uses anything but 16550+). Obviously there is an advantage that the Jetson serial UART can be switched in mode to either in order to be adaptable, so ignoring which is actually “better”, would the end result of the programming be an issue if the Jetson is using 16450 mode and the outside hardware is actually 16550?

@linuxdev - I’m only using 16450 right now, because it’s the default. Once I get FIFO working, I’ll probably switch to 16550(A) mode.

I don’t think the case is compatibility between my dev machine and the Jetson though since it works before I try enabling FIFO (directly writing to the THR register), without an issue. It’s not until I “init_serial” that things seem to break.

@linuxdev - I’m only using 16450 right now, because it’s the default. Once I get FIFO working, I’ll probably switch to 16550(A) mode.

I don’t think the case is compatibility between my dev machine and the Jetson though since it works before I try enabling FIFO (directly writing to the THR register), without an issue. It’s not until I “init_serial” that things seem to break.

“it works before I try enabling FIFO”
" It’s not until I “init_serial” that things seem to break. "

Firstly, I have to admit that I am not familiar with the 16450 and how it works.
(I have plenty of experience with NXP and ST UARTs in embedded processors)

You said it worked before - inspecting your code, it looks like the code worked in ‘polled’ mode.

Your init routine enables ‘FIFO’ mode and enables interrupts.

Then, your code tries to access the UART in ‘polled’ mode.

What is FIFO mode ?

Does FIFO mode work with interrupts (IRQs) ?

How does FIFO mode interact with IRQs ?
(What is the Interrupt Service Routine (ISR) suppose to do?)