timeout for blocking ressources

XCore Project reviews, ideas, videos and proposals.
User avatar
fabriceo
Respected Member
Posts: 275
Joined: Mon Jan 08, 2018 4:14 pm

timeout for blocking ressources

Post by fabriceo »

Hello,
Sharing a solution for handling timeout when using a ressource (channel, ports) that might be blocking the processor because its condition is not ready. This could be a channel waiting a token or accessing a port (in or out) which is triggered by a clock not available.

the select statement gives lot of possibilities with its "default" case, but I could not find a solution for ports, so here is a possible implementation with an friendly integration within a "if" statement (like setjump/longjump library).

Three routines are needed to set, test and clear the timer in charge of the timeout:

timeout_setEvent is used to prepare the timer ressource to generate the event after a given delay (in number of ticks)

timeout_running is organised so that it can be used as a condition of an "if" statement. it contains mechanism to save and restore the stack pointer in case the "if (true)" code is more complex than just accessing the port, but this is not recommended as no-one can say exactly what the compiler will generate.

timeout_clearAllEvents should be used immediately after the code accessing the blocking resource.

Code: Select all

static inline void timeout_setEvent(timer tmr, int delay) {
    asm volatile (
        "\n\t   gettime r11"                   // get time
        "\n\t   add r11,r11,%1"                // add given delay
        "\n\t   setd res[%0], r11"             // set timer target value
        "\n\t   setc res[%0], 9"               // set timer event condition after
        : : "r"(tmr),"r"(delay):"r11" );       //return result
}

static inline unsigned timeout_running(timer tmr) {
    unsigned result;
    asm volatile (
        "\n\t   ldap r11, .Levent%="           // get address of temporary label below
        "\n\t   setv res[%1], r11 "            // set resource vector address
        "\n\t   ldaw r11,sp[0]"                // get stack pointer value
        "\n\t   setev res[%1], r11"            // set environement vector to SP
        "\n\t   ldc %0, 1"                     // result will be 1
        "\n\t   eeu  res[%1]"                  // enable timer resource event
        "\n\t   setsr 1"                       // enable any events in our thread
        "\n\t   bu .Lexit%="                   // end ( go back to "if" statement )
            
        "\n .Levent%=:"                      // event entry point
        "\n\t   get r11, ed"                   // get exception data register which is the SP value set with setev
        "\n\t   set sp, r11"                   // restore stack pointer
        "\n\t   ldc %0, 0"                     // result forced to 0

        "\n .Lexit%=:"                       // exit point
        : "=r"(result) : "r"(tmr) : "r11" );   //return result
    return result;
}

static inline void timeout_clearAllEvents() {
    asm volatile("clre");
}

a typical example:

Code: Select all

    timer tmr;
    timeout_setEvent(tmr,500000000);
    int val = 0;
    if ( timeout_running(tmr) ) {
        //code for accessing the blocking resource
        myport :> val;
        //clear events imediatelly after getting port value
        timeout_clearAllEvents();
        //some basic treatment can continue here but preferably outside of the "if"
    } else {
        // timeout event is raised and can be managed here
    }
any comment or suggestion welcome!
fabriceo
User avatar
Ross
Verified
XCore Legend
Posts: 1299
Joined: Thu Dec 10, 2009 9:20 pm
Location: Bristol, UK

Post by Ross »

Hi Fabrice, could you provide an example typical use case? Thanks.
Technical Director @ XMOS. Opinions expressed are my own
User avatar
fabriceo
Respected Member
Posts: 275
Joined: Mon Jan 08, 2018 4:14 pm

Post by fabriceo »

Hi Ross
yes the typical use case which I m implementing at the moment is in lib_xua : I have situations where I m loosing the audio master clock, typically when an hardware spdif receiver does not lock on an incoming signal.
The probability that this is happening during the I2S port access (ADC or DAC or LRCLK) is about 80%.
And when the AudioHubMainLoop is blocked then we do not have the transfer interrupt with Decouple so any command sent from the host to endpoint 0 ends up nowhere and it doesn't receive the CT_END acknowledgement neither.

The approach is to start the timeout event and the "if (timeout_runing)" at the beginning of the I2S main loop.
if the timeout occurs I raise a flag and then the I2S in/out will be skipped in all the next loop, but the comand transfer is kept, with a delay equal to the normal loop time. this way with minimum changes in libxua I can handle this situation raisonably.

Additional code is needed to handle the clock recovery and restart the loop properly but this is another story.
on the usb side, I also need to monitor the Mclk presence and this is done easily in the buffer SOF routine by testing the difference with the successive getts. if the difference is 0, then I raise a local flag and just use the previous getts averaged difference, or I patch the feedback value with the theoretical 16.16 value required for the given USB host sample rate.

fabriceo