timeout for blocking ressources

XCore Project reviews, ideas, videos and proposals.
User avatar
fabriceo
Respected Member
Posts: 277
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: 1302
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: 277
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
User avatar
Ross
Verified
XCore Legend
Posts: 1302
Joined: Thu Dec 10, 2009 9:20 pm
Location: Bristol, UK

Post by Ross »

Ah okay, so a timeout for larger blocks of code that span larger than one select. One issue is it might be fragile to the compiler reseting some of the conditions on those resources. I would also make sure you pass in a new hw timer from XC otherwise it will use the timer allocated for the thread this *might* cause some issues depending on where you use the code - though maybe in esoteric usage (a timeout around a timer case or something)
Technical Director @ XMOS. Opinions expressed are my own
User avatar
fabriceo
Respected Member
Posts: 277
Joined: Mon Jan 08, 2018 4:14 pm

Post by fabriceo »

hi
well, I could not find a way to use select { ...} for implementing a timeout on a clocked port in case there is no clock running. may be I m wrong and you have example ??
so when you say that "span larger than one select", well this not really my context then.

you have a good point with the extra timer instead of the default thread timer but normally my proposed approach is safe, IF the code inside the "if(true)" statement is small and doesn't involve the default timer an doesn't involve a select case.

for the example of the lib_xua, there are 5 area where potentially I need to implement a timeout mechanism:

ADC Left acquisition, DAC Left output, LRCLK, ADC Right acquisition, DAC Right output

so I have the choice of creating a single "if" statement with the 5 stages included inside the "if(true)" section
or I can create 5 "if" statements, in this case the code in the if(true) is very limited.
for the time being I have a single one at the beginning of the "i2s" code section (below dsd). let see.

I will commit the modification of lib_xua In my own repo with a specific branch end of this week, it is open source and may help some other guys later in implementing hardware spdif receiver, or PLL based audio clock

thanks for your attention
fabriceo