This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CC1352R: UART (UART2) Driver Usage within Exception (HWI) Context

Part Number: CC1352R

Hello,

I am using the UART2 driver within a TI-RTOS application on the CC1352R. When a processor exception occurs, I need to safely shutdown an external device via UART before restarting the system.

I have configured the UART2 driver with readMode = UART2_Mode_NONBLOCKING and designed the application to call UART2_read() periodically. When an exception occurs, the UART2 driver is continues to transmit, but only receives the first ~40 characters that are sent in reply. I am suspicious that the underlying DMA transfer does not get restarted properly after an exception occurs.

Is it possible to use the UART2 driver from the context of an exception? If not, what does TI recommend for this scenario?

I suspect that my problem is related to the "TODO: case for !HwiP_interruptsEnabled()?" message within UART2_readTimeout() in UART2.c. Are there any plans to resolve this TODO?

Thank you,

Peter

  • Hi Peter,

    The recommendation for CC1352R is to use the watchdog to detect and restart in case of a failure. However, please note the Watchdog does not support other actions such as sending a UART message before restarting.

    Are you targeting a specific exeption?

    (Maybe it would work to use the Sensor Controller for this?)

    Cheers,

    Marie H.

  • Marie,

    Our project implements a custom abort() function that is registered as SysCallback.abortFxn in the TI RTOS configuration file for the BLE5-Stack. 
    This allows us to perform certain operations prior to restart after an exception occurs (e.g. saving critical data to an external SPI flash). It is within this custom abort() function that we must also send and receive UART data to safely power-off an external device.

    Our problem is that we cannot properly receive UART data once an exception has occurred, presumably because the RX functionality of the UART2 driver was not designed to be used outside of a task context. (See the "TODO: case for !HwiP_interruptsEnabled()?" message within UART2_readTimeout() in UART2.c.)

    What solution does TI recommend to continue sending and receiving UART within the context of an exception (i.e. not a Task but a HWI)?

    We have also implemented the watchdog as you suggested, but that should only be used as a last resort if the processor is completely stuck or otherwise unable to handle an exception. We must restart our device cleanly whenever possible.

    Thank you,
    Peter

  • Hi Peter,

    There shouldn’t be an issue with using the UART2 driver within the context of a SWI or HWI.

    Is there a particular reason for using Non-blocking mode? Have you considered using Callback mode?

    Another question is, how many bytes do you expect to receive in the reply? If it is a known amount, I’d advise you to use UART2_ReadReturnMode_FULL as well.

    BR,
    Andres

  • Andres,

    I would prefer to use blocking mode for its simplicity; however, the documentation in UART2.h very clearly states that:

    The context of calling UART2_read() and UART2_write() in blocking mode must always be a Task.

    Since I want to use this UART2 instance outside of a Task context (within a HWI), I have chosen UART2_Mode_NONBLOCKING instead. I could try UART2_Mode_CALLBACK if you believe that it would resolve my problem, but I don't otherwise need the extra complexity that callbacks provide.

    I do not know exactly how many bytes the external device is going to send to the CC1352, so I don't think that UART2_ReadReturnMode_FULL would be helpful.

    ----

    I will ask again: Are there plans to resolve the following TODO in UART2_readTimeout?

    // TODO: case for !HwiP_interruptsEnabled()?

    The presence of this TODO leads me to believe that UART2_readTimeout will not operate properly when hardware interrupts are disabled, as is the case when called from an exception. This is supported by the fact that UART2_writeTimeout has special logic to handle this case:

    if (!HwiP_interruptsEnabled()) {
        /* RTOS not started yet, called from main(), use polling mode */
    ...

    I will reiterate the fact that I can receive some UART data within my exception handler; but eventually the reception stops, presumably because interrupts are disabled. What must I do to continue receiving UART data when interrupts are disabled?

    Thank you again for your assistance.
    Peter

  • Hi Peter,

    I understand why blocking mode is not suitable for your particular application. The reason I mentioned Callback mode is because I have successfully used it before in both Swi and Hwi contexts.

    Furthermore, it's fine if you want to use UART2_ReadReturnMode_PARTIAL. Just be aware that Return Partial mode is implemented such that it unblocks or performs a callback whenever a read timeout error occurs on the UART peripheral (the read timeout occurs if the read FIFO is non-empty and no new data has been received for a number of clock cycles).  Sometimes it happens that the data received by the UART has some gaps between the characters and this can be enough to trigger the end of the read operation.

    I will ask again: Are there plans to resolve the following TODO in UART2_readTimeout?

    // TODO: case for !HwiP_interruptsEnabled()?

    To be honest, I don't know the reason for that specific comment in the driver. But what I can say is that I have successfully used the UART in a Hwi context.

    BR,
    Andres

  • Andres,

    I've created a simple program based on TI's uart2echo example that should allow you to reproduce the problem I'm experiencing. If you have time, I'd appreciate it if you could try it out!

    Example Setup:

    1. Import the uart2echo project through the Project Wizard for TIRTOS and the CCS compiler (uart2echo_CC1352R1_LAUNCHXL_tirtos_ccs)
    2. Replace the uart2echo.c source file with the modified file that I have attached to this post.
      /*
       * Copyright (c) 2020, Texas Instruments Incorporated
       * All rights reserved.
       *
       * Redistribution and use in source and binary forms, with or without
       * modification, are permitted provided that the following conditions
       * are met:
       *
       * *  Redistributions of source code must retain the above copyright
       *    notice, this list of conditions and the following disclaimer.
       *
       * *  Redistributions in binary form must reproduce the above copyright
       *    notice, this list of conditions and the following disclaimer in the
       *    documentation and/or other materials provided with the distribution.
       *
       * *  Neither the name of Texas Instruments Incorporated nor the names of
       *    its contributors may be used to endorse or promote products derived
       *    from this software without specific prior written permission.
       *
       * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
       * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
       * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
       * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
       * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
       * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
       * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
       * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
       * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
       * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
       * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
       */
      
      /*
       *  ======== uart2echo.c ========
       */
      #include <stdint.h>
      #include <stddef.h>
      
      /* Driver Header files */
      #include <ti/drivers/GPIO.h>
      #include <ti/drivers/UART2.h>
      #include <ti/sysbios/BIOS.h>
      #include <ti/sysbios/knl/Task.h>
      
      /* Driver configuration */
      #include "ti_drivers_config.h"
      
      UART2_Handle uart;
      UART2_Params uartParams;
      
      void echo(uint32_t * excStack, uint32_t lr)
      {
          char input;
          size_t bytesRead;
          size_t bytesWritten = 0;
      
          /* Loop forever echoing */
          while (1) {
              bytesRead = 0;
              while (bytesRead == 0) {
                  UART2_read(uart, &input, 1, &bytesRead);
              }
      
              bytesWritten = 0;
              while (bytesWritten == 0) {
                  UART2_write(uart, &input, 1, &bytesWritten);
              }
      
      
              // Trigger an exception if this is a task context
              if((input == 'e') && (BIOS_getThreadType() == BIOS_ThreadType_Task))
              {
                  const char * exceptionMsg = "Triggering an exception!\r\n";
                  UART2_write(uart, exceptionMsg, strlen(exceptionMsg), NULL);
                  *((char *)NULL) = 5;
              }
          }
      }
      
      /*
       *  ======== mainThread ========
       */
      void *mainThread(void *arg0)
      {
          /* Call driver init functions */
          GPIO_init();
      
          /* Configure the LED pin */
          GPIO_setConfig(CONFIG_GPIO_LED_0, GPIO_CFG_OUT_STD | GPIO_CFG_OUT_LOW);
      
          /* Create a UART where the default read and write mode is BLOCKING */
          UART2_Params_init(&uartParams);
          uartParams.baudRate = 115200;
          uartParams.readMode = UART2_Mode_NONBLOCKING;
          uartParams.writeMode = UART2_Mode_NONBLOCKING;
      
          uart = UART2_open(CONFIG_UART2_0, &uartParams);
      
          if (uart == NULL) {
              /* UART2_open() failed */
              while (1);
          }
      
          /* Turn on user LED to indicate successful initialization */
          GPIO_write(CONFIG_GPIO_LED_0, CONFIG_GPIO_LED_ON);
      
          const char   echoPrompt[] = "Echoing characters:\r\n";
          UART2_write(uart, echoPrompt, sizeof(echoPrompt), NULL);
      
          echo(NULL, 0);
      }
      
    3. Open the release.cfg file from the tirtos_builds_cc13x2_cc26x2_release_ccs parent project and update the m3Hwi exception settings so that m3Hwi.excHookFunc points to the echo() function in uart2echo.c:
      m3Hwi.enableException = true;
      //m3Hwi.enableException = false;
      //m3Hwi.excHandlerFunc = null;
      m3Hwi.excHookFunc = "&echo";

    At this point, the project should build and you can load it onto a Launchpad.

    Behavior:

    This version of the uart2echo has been modified to trigger an exception when `e` is sent via UART, and then keep echoing back UART data within the exception context.

    When I run this program on my LaunchPad, I observe the following behavior:

    • Prior to triggering an exception: Characters that I send through the USB UART interface are echoed back with minimal latency.
    • After sending 'e' to trigger an exception: Characters are echoed back in "chunks". (I have to type a handful of characters before anything is returned.)

    This behavior causes a problem in my application because the device I'm communicating with eventually stops transmitting, and then I don't receive the last part of the most recent message.

    I will keep digging to see if I can figure out why UART2 behaves this way within an exception. I would appreciate any feedback that you might have!

    Thank you and have a great weekend!
    Peter

  • Hi Peter,

    Sorry for the late reply. I was out-of-office until today.

    I will attempt to reproduce what you are seeing and will get back to you.

    BR,
    Andres

  • Hi Peter,

    First of all, thank you for the patience and the code snippet. I did reproduce what you described.

    At first I didn't understand what was happening, because as I mentioned before I have used the UART2 driver without any issues from a Hwi context. But it was a mistake on my part to also assume that it would work on a Hardware exception context.

    Now, the main issue here is that the Hwi_excHookFunc() runs on the ISR stack and it is called without any task or Swi protection. And since the UART2 driver relies on task scheduling operations like semaphores, it is not safe to use the driver in this context.

    This is mentioned in the TI-RTOS documentation.

    https://dev.ti.com/tirex/explore/content/simplelink_cc13x2_26x2_sdk_5_20_00_52/docs/tirtos/sysbios/docs/cdoc/index.html#ti/sysbios/family/arm/m3/Hwi.html#exc.Hook.Func

    This same limitation also applies to the use of the watchdog timer for instance. Therefore, you would have to find another way to notify the external device of the exception. 

    BR,
    Andres

  • Hi Peter,

    Just an update on my last reply. I asked RnD to see what the best approach would be for this particular use case. They agree that your best bet would be to either write a basic bare-metal UART driver, or to use the UART APIs available in DriverLib.

    If you get into the exception handler, it is because something has gone seriously wrong (this includes something that could have compromised the kernel). And since the UART2 driver relies heavily on the kernel, its behavior might not be what you expect.

    BR,
    Andres

  • Thank you for your help Andres! I will look into using the DriverLib APIs.

    Peter