This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

nrf52832 uart tx works only with NRF_UARTE0 read after EndTx

FormerMember
FormerMember

Greetings, There is a bit of strange operation in UARTE peripheral in nrf52832 with the DK. In the simple code below, if both of the if statements with comments 'Check 1' and 'Check 2' are removed the 'hello' prints are not sent and I get some junk value. If either of the two reads to the UARTE registers is made in these if statement, then the 'hello' prints are sent out correctly. What's happening? I don't see any errata related to UARTE peripheral.

Thanks.

Toolchain: GCC 4.9.3q2 on Linux

#include <stdbool.h>
#include <stdint.h>
#include "nrf_delay.h"
#include "nrf52-gpio.h"
#include "nrf52-clock.h"
#include "board.h"
#include "nrf52-uart.h"

int main(void)
{
	nrf_gpio_cfg_output(LED1);
	nrf_gpio_cfg_output(LED2);
	nrf_gpio_cfg_output(LED3);
	nrf_gpio_cfg_output(LED4);

	/* Configure TX and RX pins from board.h */
	nrf_gpio_cfg_output(TX_PIN_NUMBER);
	nrf_gpio_cfg_input(RX_PIN_NUMBER, GPIO_PIN_CNF_PULL_Disabled);
	NRF_UARTE0->PSEL.TXD = TX_PIN_NUMBER;
	NRF_UARTE0->PSEL.RTS = RX_PIN_NUMBER;

	nrf_gpio_cfg_output(RTS_PIN_NUMBER);
	nrf_gpio_cfg_input(CTS_PIN_NUMBER, GPIO_PIN_CNF_PULL_Disabled);
	NRF_UARTE0->PSEL.RTS = RTS_PIN_NUMBER;
	NRF_UARTE0->PSEL.CTS = CTS_PIN_NUMBER;
	NRF_UARTE0->CONFIG = (UARTE_CONFIG_HWFC_Enabled << UARTE_CONFIG_HWFC_Pos);

	/* Configure other UART parameters, BAUD rate is defined in nrf52-uart.h	*/
	NRF_UARTE0->BAUDRATE = (UARTE_BAUDRATE << UARTE_BAUDRATE_BAUDRATE_Pos);
	NRF_UARTE0->ENABLE = (UARTE_ENABLE_ENABLE_Enabled << UARTE_ENABLE_ENABLE_Pos);

    // Toggle LEDs.
    while (true)
    {
    	uint32_t i;
        for (i = 0; i < 2; i++)
        {
        	if(i){
        		NRF_P0->OUTSET = (1<<LED1);
        	} else {
        		NRF_P0->OUTCLR = (1<<LED1);
        	}
        	nrf_delay_ms(500);

            uint8_t str[] = "hello\n";
            uint32_t len = 6;

            NRF_UARTE0->EVENTS_ENDTX = 0;
            NRF_UARTE0->TASKS_STOPTX = 0;

        	NRF_UARTE0->TXD.PTR = (uint32_t)((uint8_t *) str);
        	NRF_UARTE0->TXD.MAXCNT = (uint32_t) len;

        	NRF_UARTE0->TASKS_STARTTX = 1;

        	while((0 == NRF_UARTE0->EVENTS_ENDTX)&&(0 == NRF_UARTE0->EVENTS_TXSTOPPED)){}

            /*****Check 1*****/
        	if(NRF_UARTE0->EVENTS_ENDTX){
        		NRF_P0->OUTSET = (1<<LED3);
        	}

            /*****Check 2*****/    
        	if(6 == NRF_UARTE0->TXD.AMOUNT){
        		NRF_P0->OUTSET = (1<<LED2);
        	}

            NRF_UARTE0->EVENTS_ENDTX = 0;
            NRF_UARTE0->TASKS_STOPTX = 0;
        }
    }
}
  • There's no NRF dependency in the code at all, neither 51 nor 52. It would do the same on either. However the 51 doesn't have data pointers for uart, just a Tx register which takes a byte, so there's not an equivalent case. I don't know if it's a bug. The register is defined as volatile uint32, I don't know what inference it's allowed to draw about the use of data passed to it. I suspect if we report it as a gcc bug someone may explain why initializing stack based strings and passing them to registers is undefined behavior. It's odd that char*works but char[] doesn't.

  • No it doesn't end up in flash. It doesn't end up anywhere in the binary. And apart from that you can see it's not loaded and the SP is what's set into the tx ptr uninitialized.

  • I meant to ask if it did the same on Cortex-M0, because we tell the gcc compiler about the cpu explicitly I just verified it and it does the same thing. I tested it with this code on nRF51 (cortex-M0)

    int main(void)
    {
        char str[] = "hello\n";
        NRF_UART0->TXD = (uint32_t)str;
    }
    
  • I went to file this as a bug against ARM GCC to find you it's 'highly recommended' to ask a question in the answers section before filing a new bug, so I've done that. I will now probably get my head shot off by the GCC C-heads for writing invalid C, but that's life.

    Prithvi - I've been working on this at the asm level, not actually testing it - can you try

    1. changing the uint8_t[] to a char * pointer
    2. alternatively adding 'volatile' to that uint8_t[] str pointer (although that' stupid"

    and seeing if that actually also works around your underlying issue as well as doing the reads does? I'm concerned that we may have found an issue, but that may not actually be your issue!

  • FormerMember
    0 FormerMember in reply to RK

    RK, I like your Yoda sentence 'Bug should I file?' :) I'm glad to have been at least the spark in finding a bug in GCC.

    I don't have any issue now because the code in my question is just when I was playing around to understand the UARTE peripheral. Now I have made a ping-pong buffers based driver and It works well. I'll make it open source in a few days.

    In any case I'll do the tests that you suggested in some time. And I'll play around seeing how this will affect the original issue, i.e. it working only when there is some UARTE register read or delay after the while loop waiting for EVENT_ENDTX.

Related