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;
        }
    }
}
Parents
  • Not really an answer but the comments are too restricted.

    I have a shorter version which exhibits the same optimisation 'issue'

    #include <stdint.h>
    
    struct UART
    {
        __volatile uint32_t padding[200];
        __volatile uint32_t TXD;
    };
    
    #define NRF_UARTE0 ((struct UART*)0x40002000)
    
    int main(void)
    {
        char str[] = "hello\n";
        NRF_UARTE0->TXD = (uint32_t)str;
    }
    

    when compiled main looks like this

    00000000 <main>:
       0:   4b03            ldr     r3, [pc, #12]   ; (10 <main+0x10>)
       2:   b082            sub     sp, #8
       4:   2000            movs    r0, #0
       6:   f8c3 d320       str.w   sp, [r3, #800]  ; 0x320
       a:   b002            add     sp, #8
       c:   4770            bx      lr
       e:   bf00            nop
      10:   40002000        .word   0x40002000
    

    What's happening is space is reserved on the stack for 'str' and that address is put into TXD but it's never initialised with 'hello\n"

    An easy workaround is to change char str[] = "hello\n"; to char *str="hello\n";, then the output looks like this (making the variable volatile does the same thing)

    00000000 <main>:
       0:   4b02            ldr     r3, [pc, #8]    ; (c <main+0xc>)
       2:   4a03            ldr     r2, [pc, #12]   ; (10 <main+0x10>)
       4:   f8c3 2320       str.w   r2, [r3, #800]  ; 0x320
       8:   2000            movs    r0, #0
       a:   4770            bx      lr
       c:   40002000        .word   0x40002000
      10:   00000000        .word   0x00000000
    

    The contents of the address at main+0x10 is fixed up at link time to point to the .rodata initialised string.

    This is compiled at -Os however -O1 does the same (as does -O2).

    So what appears to be happening is space is reserved for the string on the stack, but it's not initialised. If you make the string longer you get more space reserved (ie sub sp, #8 becomes sub sp, #).

    If you call strlen() on it, it initialises the string properly. So gcc has decided that it needs to allocate space on the stack for the string, however the contents isn't used so it doesn't bother to initialise it. This may or may not be related to how the register is defined at uint32_t but changing it around hasn't helped.

    Is this a gcc bug? If it is it's not recent, I tried it with last year's Q1 release and it's still there. I think it's a bug myself and the memory should be initialised but there may be some bizarre C rule I don't know which makes that code undefined and allows the compiler to make the choice it does.

  • 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!

Reply
  • 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!

Children
No Data
Related