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.

EVM430-FR6043: Avoid USS_initAlgorithms() delay in subsequent boots, cache configuration data in FRAM

Part Number: EVM430-FR6043

Tool/software:

Hi everyone,

I'm designing an OEM airflow meter based on the EVM430-FR6043. The firmware works and measures just fine, but I've discovered that it always takes around 400ms to start up, which is a fairly long time for a "simple" sensor. Using some debug pin outputs and a scope, I was able to nail it to the `USS_initAlgorithms()` function, which is executed right before my code starts and takes most of this initialization time.

Given that it only takes the configuration structure and is named initAlgorithms(), I'd assume that it really only precalculates some values, which could certainly be cached in the FRAM to get this delay only during the very first power-on of the MCU after it has been programmed. The `_USS_SW_Library_configuration_` structure indeed already contains a `validationKey` field and is located in persistent memory (FRAM), so it looks like the library was designed with this in mind.

What I tried is to run the initialization function only once when there is no valid key and skip the initialization in all subsequent boots:

    if (gUssSWConfig.validationKey != CONFIG_VALIDATION_KEY) {
        code = USS_initAlgorithms(&gUssSWConfig);
        checkCode(code, USS_message_code_no_error);
        gUssSWConfig.validationKey = CONFIG_VALIDATION_KEY;
    }

This succeeds in skipping the initialization (and the delay) in every but the first boot, but it also breaks the application. As soon as `USS_runAlgorithms()` is called in this case, the MCU crashes and restarts.

I've already checked the __no_init memory buffers using the debugger before and after the run of `USS_initAlgorithms()` to check if it initializes some data there, but did not discover a change.

So my questions:

  • What does `USS_initAlgorithms()` do that takes so long?
  • What data does it modify that is not persisted in FRAM?
  • Is there a way to get rid of this delay using the described caching approach or anything else?

Thanks!

Best regards,
Philipp

  • Hi,

    Sorry for the late response. I am on a vocation before. 

    Your idea looks very interesting and practical. I do know the `USS_initAlgorithms()` will take some time, but haven't look at exactly how long it takes before. 

    A roughly check on what has been done in USS_initAlgorithms.

    1. validate parameters

    2. Set parameters like filters, algorithm parameters.

    3. call USS_init() and USS_updateClockRelativeError()

    It seems these modifications only happen in FRAM. But there is a USS_lea_init(); calling inside the USS_init() which configures the LEA peripheral. You can try if adding this USS_lea_init() could solve your issue. 

    Best regards,

    Cash Hao

  • Hi Cash Hao,

    thanks for your response. This sounds very promising. However, I don't have a call signature for neither USS_init() nor USS_lea_init(). My HW is currently in use, so I can't test if simply calling USS_lea_init(&gUssSWConfig) works, but in any case I'd rather like to have the official signature. Could you provide this?

    Best regards,
    Philipp

  • Hi,

    What do you mean by offering an official signature? 

    Best regards,

    Cash Hao

  • Hi Cash Hao,

    I just mean the correct call signature for the function that would be placed in the header file. Something like "USS_message_code USS_lea_init(USS_SW_Library_configuration *config);" or similar.

    Best regards,
    Philipp

  • Hi,

    I can send you the function code below. 

    USS_FRAM_FUNCTION(USS_lea_init)
    void USS_lea_init(void)
    {
    // Initialize LEA registers
    LEACNF0 = LEALPR | LEAILPM;
    LEACNF1 = 0;
    LEACNF2 = LEAMT >> 2;
    LEAPMS1 = 0;
    LEAPMS0 = 0;
    LEAPMDST = 0;
    LEAPMCTL |= LEACMDEN;
    LEAIE |= LEAPMCMDIE;
    LEACMCTL = 0;
    USS_LEA_IFG = 0;

    return;
    }

    Best regards,

    Cash Hao

  • Hi Cash Hao,

    great, thanks! I'll test it as soon as I have again access to the hardware.

    Best regards,
    Philipp

  • Hi Cash Hao,

    I tried your suggestion, but with no luck. The MCU runs just fine after programming it, but after a reset, when it skips USS_initAlgorithms() and only runs USS_lea_init(), it crashes when USS_runAlgorithms() is called for the first time. This happens no matter whether I call the USS_lea_init() implementation already in the USS library or the implementation that you posted. So there must be more configuration work that's missing.

    I don't know what exactly happens during the failure. I just see that our application doesn't work anymore when the program is running freely and when I single-step it with the debugger, the call to USS_runAlgorithmsFixedPoint() never returns. I don't have any more details, as the code below is completely opaque inside the compiled library.

    FWIW, that's my current implementation:

    // This is where I would normally just call USS_initAlgorithms() unconditionally.
    
        if (gUssSWConfig.validationKey != CONFIG_VALIDATION_KEY) {
            code = USS_initAlgorithms(&gUssSWConfig);
            checkCode(code, USS_message_code_no_error);
            gUssSWConfig.validationKey = CONFIG_VALIDATION_KEY;
        } else {
            USS_lea_init();
        }
    

    Any more ideas?

    Thanks and best regards,
    Philipp

  • Hi,

    Get your feedbacks. 

    I will debug it on my site next week and check what I can find here. 

    Best regards,

    Cash Hao

  • Hi Cash Hao,

    are there any updates on this?

    Best regards,
    Philipp

  • Hi,

    Sorry for the late response. 

    I have read out the memory data before and after calling USS_initAlgorithms() function. 

    The differences on the memory are some peripherals register and FRAM configuration. There are no differences in RAM section. According to this result, it should be able to bypass this function. I do not have more clues on this behavior. 

    https://e2e.ti.com/cfs-file/__key/communityserver-discussions-components-files/166/before-inithttps://e2e.ti.com/cfs-file/__key/communityserver-discussions-components-files/166/after-init

    Best regards,

    Cash Hao

  • Hi Cash Hao,

    it is certainly a good idea to just dump the whole memory contents before and after the call. However, when analyzing the differences, I'm unsure about the interpretation:

    • In the peripheral section, I see differences in the registers of the SYS (0180h), Digital I/O (0200h), TA1 (0380h), TA2 (0400h), MPY32 (04C0h), DMA (0500h), LEA (0A80h). This leads me to believe that the dumps were not taken immediately before and after the call to USS_initAlgorithms(), but also show other initialization work.
    • The byte at 01860h was changed from 0x00 to 0x76. This location is not documented at all in the datasheet, it would lie between the BSL (ROM) and TI calibration and configuration data (FRAM). Do you have an idea what this could be?
    • Quite some changes are in the 2xxxh range, which should be system (not LEA) RAM, if I interpret the datasheet and linker file correctly.

    Anyway, I find the idea of just dumping the whole memory before and after the call quite appealing, this should make it possible to skip the long initialization. At least as long as I can figure out to which symbol some changes belong, which appears to be a challenge already.

    When I do the same with my current code and really stop the debugger immediately before and after the call to USS_initAlgorithms(), I get some differences in data structures that I can't locate in the code:

    • gUSSAlgObject
    • gHilbertFilterConstant

    Is there any documentation about these symbols and especially their size? I can certainly reference them in the code, but without having their size, it is problematic to save and restore them in FRAM.

    Thanks and best regards,
    Philipp

  • Hi Philipp,

    1. No idea what is saved in 0x1860. It could be a hidden register but I do not know what it is. 

    2. Oh, yes. There is some change in the system RAM. I only focus on the RAM shared with LEA before. So, the init function do changes some parameters in RAM. 

    3.  gUSSAlgObject starts from 0x21A0 to 0x22AC

     gHilbertFilterConstant starts from 0x4F68 to 0x4F80

    Best regards,

    Cash Hao

  • Hi Cash Hao,

    thanks for the inputs. I've now managed to get a working setup with this code:

    /*
     * A replacement for USS_initAlgorithms() that only runs the
     * full initialization during the first boot and then caches
     * the results in FRAM for faster subsequent boots.
     *
     * NOTE: The internal workings of the USS library are not
     * really documented and it needed some reverse engineering
     * to get this to work:
     * 1. Take full memory dump before USS_initAlgorithms()
     * 2. Take full memory dump after USS_initAlgorithms()
     * 3. Find differing data
     * 4. Use linker map file to find out to which symbols the
     *    differences belong.
     * 5. Use `nm -S USS_SW_CCS_large_code_large_data.lib` to find
     *    the size of each of these symbols.
     * For this reason, the implementation is rather fragile and
     * may fail if USS parameters are changed, as these may cause
     * other symbols to become relevant.
     *
     * See also: 
     * https://e2e.ti.com/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/1507600/evm430-fr6043-avoid-uss_initalgorithms-delay-in-subsequent-boots-cache-configuration-data-in-fram/
     */
    extern void USS_lea_init(void);
    extern void *gFilterConstant;
    extern void *gHilbertFilterConstant;
    extern void *gUSSAlgObject;
    typedef struct uss_init_buffer_ {
        uint8_t gFilterConstant[0x28];
        uint8_t gHilbertFilterConstant[0x18];
        uint8_t gUSSAlgObject[0x116];
    } uss_init_buffer_t;
    #pragma PERSISTENT(persist_uss_init_buffer)
    static uss_init_buffer_t persist_uss_init_buffer = {0};
    static void uss_init_quick(void) {
        if (gUssSWConfig.validationKey != CONFIG_VALIDATION_KEY) {
            USS_initAlgorithms(&gUssSWConfig);
            /* Store initialized data to nonvolatile memory (FRAM). */
            memcpy(persist_uss_init_buffer.gFilterConstant, &gFilterConstant, sizeof(persist_uss_init_buffer.gFilterConstant));
            memcpy(persist_uss_init_buffer.gHilbertFilterConstant, &gHilbertFilterConstant, sizeof(persist_uss_init_buffer.gHilbertFilterConstant));
            memcpy(persist_uss_init_buffer.gUSSAlgObject, &gUSSAlgObject, sizeof(persist_uss_init_buffer.gUSSAlgObject));
            /* Take the shortcut on next boot. */
            gUssSWConfig.validationKey = CONFIG_VALIDATION_KEY;
        } else {
            USS_lea_init();
            /* Initialize LEA hardware, but otherwise just restore precalculated
             * data from nonvolatile memory. */
            memcpy(&gFilterConstant, persist_uss_init_buffer.gFilterConstant, sizeof(persist_uss_init_buffer.gFilterConstant));
            memcpy(&gHilbertFilterConstant, persist_uss_init_buffer.gHilbertFilterConstant, sizeof(persist_uss_init_buffer.gHilbertFilterConstant));
            memcpy(&gUSSAlgObject, persist_uss_init_buffer.gUSSAlgObject, sizeof(persist_uss_init_buffer.gUSSAlgObject));
        }
    }

    Unfortunately, I've only now figured out that I made a mistake during my initial profiling. USS_initAlgorithms() takes its time, but only a few 100us. The really substantial delay comes from USS_configureUltrasonicMeasurement() and more specifically, from the calculation of the transmit pulse patterns in commonConfigureMultiTone(). But at least "fixing" this is fairly easy: Since the results are stored in the persistent USS configuration data, it is enough to put an "if" around the slow code:

    #if defined(__MSP430_HAS_SAPH_A__) && (USS_PULSE_MODE == USS_PULSE_MODE_MULTI_TONE)
    USS_message_code commonConfigureMultiTone(USS_SW_Library_configuration *config)
    {
    
        USS_Pulse_Multitone_Configuration* multConf;
        int32_t i,f1Freq,f2Freq,pulseCenterFreq, pulseBandwidth;
        uint32_t fSinInc, pPGFreq;
        _iq30 iq30Btemp;
        _iq23 iq23B;
        _iq18 iq18FIncrease;
        _iq12 iq12PulseDurFreq, iq12pPGCycles;
        _iq10 iq10FIncreased;
        uint16_t *pCurrArray;
        int16_t dmaCycles, xCycles,eCycles, stopAddr,pPGCycles;
        uint8_t ehperIndex, elperIndex, xhperIndex, xlperIndex,currentState;
        int8_t currentValue, previousValue;
    
        multConf =
                (USS_Pulse_Multitone_Configuration*) config->measurementConfig->pulseConfig->pToneConfig;
    
        /* The calculation of the pulse streams takes a considerable amount of
         * time (approx. 400ms), but the results are going into FRAM, and are
         * persistent anyway. So it is fine to just skip the calculation except for
         * the first boot after a firmware download. */
        if (config->validationKey != CONFIG_VALIDATION_KEY) {
    
            // Note: It is assumed that f1Freq<f2Freq
            f1Freq = (int32_t)config->measurementConfig->pulseConfig->F1Frequency;
            f2Freq = (int32_t)config->measurementConfig->pulseConfig->F2Frequency;
    
            // Calculate the excitation pulse bandwidth
            pulseBandwidth = (f2Freq -f1Freq);
    
            // ... more code ...
            
            }
    
    
            privatePadMultiToneConfig(config, multConf);
        }  // End of skipping for faster boots, the remaining code must stay.
    
    
     /*
      * Configure e-pulses and x-pulses RAM memory
      */
    
        eCycles = (multConf->numOfTrillcycles);
        xCycles = (multConf->numOfTrillcycles) -1;
        dmaCycles = eCycles + xCycles;
        stopAddr = (xCycles)*6+3;
    
        // ...

    Now our sensors start providing data after about 30ms. Still not instant, but much more reasonable than half a second.

    Thanks again!

    Best regards and have a nice weekend,
    Philipp

**Attention** This is a public forum