Clearing pending event in selects

Technical questions regarding the XTC tools and programming with XMOS.
mculibrk
Active Member
Posts: 38
Joined: Tue Jul 13, 2010 2:57 pm

Clearing pending event in selects

Post by mculibrk »

I have a question regarding selects when waiting for multiple events.
For example

Code: Select all


void f(streaming chanend ch, in port CLK, in port DATA) {
unsigned cmd;
unsigned clkState, dataState;
unsigned tmp, counter;

clkState = peek(CLK);
dataState = peek(DATA);

counter = 5;
while(counter) {
#pragma ordered
select {
  case ch :> cmd: 
    ...do something with cmd
   break;
  case CLK when pinsneq(clkState) :> clkState:
     DATA :> tmp;
     counter--;
     ... do something more
    break;
   case DATA when pinsneq(dataState) :> dataState:
     DATA :> cmd;
     ch <: cmd;
    ...other actions
    break;
  }
}
If I understand the select statement correctly, event handlers will be configured for all resources used in select but just one will be executed as the event appears.

So, if both CLK and DATA changes simultaneously both event handlers will be executed, right? First the part that handles the CLK change and then, in the next while cycle, the DATA handler.

it's possible to use some guard flags for that in the "DATA handler" but I'm wondering if the pending events on a resource can be "cleared"? I know the event handler for the resource could be "cleared" but that would break the select (the handler should be manually set again - to the correct place!)

I also saw a construct in few places like this one lilltroll showed here

Code: Select all

         case t when timerafter(time) :> int _: //Do not run another event
         t when timerafter(time+TIMERESERVE) :> int _; //if more than TIMERESERVE CPU cycles are available
...
what exactly that _: in the case does?

regards,
mculibrk


User avatar
lilltroll
XCore Expert
Posts: 956
Joined: Fri Dec 11, 2009 3:53 am
Location: Sweden, Eskilstuna

Post by lilltroll »

Code: Select all

case t when timerafter(time) :>
will return 32 bits of data.
Since I do not want to store the data in this case, it is just a temporary construction.

I try to write :> int _void to be less confusing nowadays.
(I'm not really familiar with naming conventions yet.)

And :oops: , it should be less cycles available, meaning do not start processing another event if there is not enough CPU cycles to process that entire other event before the time-critcal event occurs.
Probably not the most confused programmer anymore on the XCORE forum.
User avatar
segher
XCore Expert
Posts: 844
Joined: Sun Jul 11, 2010 1:31 am

Post by segher »

mculibrk wrote:If I understand the select statement correctly, event handlers will be configured for all resources used in select but just one will be executed as the event appears.
Correct.
So, if both CLK and DATA changes simultaneously both event handlers will be executed, right?
No, only one of them.
mculibrk
Active Member
Posts: 38
Joined: Tue Jul 13, 2010 2:57 pm

Post by mculibrk »

lilltroll wrote:

Code: Select all

case t when timerafter(time) :>
will return 32 bits of data.
Since I do not want to store the data in this case, it is just a temporary construction.

I try to write :> int _void to be less confusing nowadays.
(I'm not really familiar with naming conventions yet.)

And :oops: , it should be less cycles available, meaning do not start processing another event if there is not enough CPU cycles to process that entire other event before the time-critcal event occurs.
Aaaa... that's the famous _.... I thought it was some "undocumented XC feature/directive" which somehow modifies the behavior of the select/case...

I usually do (I saw it described in the "Programming XC on XMOS devices" book, to be correct)

Code: Select all

     case t when timerafter(time) :> void:
     ....
which I find pretty understandable (without defining any new variables etc)
mculibrk
Active Member
Posts: 38
Joined: Tue Jul 13, 2010 2:57 pm

Post by mculibrk »

Hmm...
segher wrote:
mculibrk wrote:So, if both CLK and DATA changes simultaneously both event handlers will be executed, right?
No, only one of them.
this is something I would really understand better as it could lead to some missed events or wrongly detected ones.

First of all, the "double event" thing I described is meant exclusively in a loop (while or something). As I understood the docs (section 2.7 page 21), both events should be "detected" but only the one of the case statements will be "served" leaving the other event "detectable" in the next loop.

If the entire "event detection/events" gets cleared upon executing a case statement and the whole "detection" is again setup on entering the select in the next loop, some events could be easily missed if the previous case statement takes "some time to complete". Am I right? ...or talking nonsenses again?

An example - detect edges on two (or more) ports:

Code: Select all


unsigned lastA, lastB;

lastA = peek(PORTA);
lastB = peek(PORTB);

while (1) {
  select {
    case PORTA when pinsneq(lastA) :> lastA:
      do_process_edge_on_port_A();
      break;
    case PORTB when pinsneq(lastB) :> lastB:
      do_process_edge_on_port_B();
      break;
    }
}
For example if in the code snippet both ports change simultaneously the first case gets triggered and do_process_edge_on_port_A() is executed. But if the PORTB changes again during execution of the do_proc....A() the portB event would not be detected???

regards,
mculibrk
User avatar
daveg
Member++
Posts: 28
Joined: Thu Dec 10, 2009 7:25 pm

Post by daveg »

mculibrk wrote:Hmm...
segher wrote:
mculibrk wrote:So, if both CLK and DATA changes simultaneously both event handlers will be executed, right?
No, only one of them.
this is something I would really understand better as it could lead to some missed events or wrongly detected ones.

First of all, the "double event" thing I described is meant exclusively in a loop (while or something). As I understood the docs (section 2.7 page 21), both events should be "detected" but only the one of the case statements will be "served" leaving the other event "detectable" in the next loop.
Unused events will still be waiting and another select will event on them, so yes they're still "detectable" in the next loop iteration.
An example - detect edges on two (or more) ports:

Code: Select all


unsigned lastA, lastB;

lastA = peek(PORTA);
lastB = peek(PORTB);

while (1) {
  select {
    case PORTA when pinsneq(lastA) :> lastA:
      do_process_edge_on_port_A();
      break;
    case PORTB when pinsneq(lastB) :> lastB:
      do_process_edge_on_port_B();
      break;
    }
}
For example if in the code snippet both ports change simultaneously the first case gets triggered and do_process_edge_on_port_A() is executed. But if the PORTB changes again during execution of the do_proc....A() the portB event would not be detected???
The portB event would be caught the next time around the loop, but if you spend too long processing an event then you can indeed lose incoming data by not getting back to the select quickly enough. This is why you'd tend to use multiple threads and buffering between them, so that you can have a select listening the whole time in a tight loop and shuttling data off to some other thread(s) for processing, with a bit of buffering to avoid losing data on close-together events.

(Btw, if the events are at exactly the same time then I don't think that you can guarantee which will be chosen, but if you set #pragma ordered as in your previous example then they are enabled one at a time in the order they appear in the select, so whichever one is waiting will be hit first)
mculibrk
Active Member
Posts: 38
Joined: Tue Jul 13, 2010 2:57 pm

Post by mculibrk »

daveg wrote:
Unused events will still be waiting and another select will event on them, so yes they're still "detectable" in the next loop iteration.

The portB event would be caught the next time around the loop, but if you spend too long processing an event then you can indeed lose incoming data by not getting back to the select quickly enough.
So, if I understand correctly, the case events are not "buffered" in sense of a "event flag" per resource?
Please confirm/correct my reasoning:

- by entering a select block each resource found this block is initialized to respond to requested events
- select "blocks" with waiteu (if no default case exists) or executes enable events/disable events to just "catch" a single event
- all event detection/status is cleared on all resources used in the select
- process the case statement (effectively servicing the event)
- continue after the select block

this results in configuring the event conditions in every single loop, right?
This is why you'd tend to use multiple threads and buffering between them, so that you can have a select listening the whole time in a tight loop and shuttling data off to some other thread(s) for processing, with a bit of buffering to avoid losing data on close-together events.
ok, but loosing an entire thread just for that could be a little too "expensive"... well, depending on the cost of "missing events" obviously.

regards,
mculibrk
User avatar
daveg
Member++
Posts: 28
Joined: Thu Dec 10, 2009 7:25 pm

Post by daveg »

mculibrk wrote: So, if I understand correctly, the case events are not "buffered" in sense of a "event flag" per resource?
Please confirm/correct my reasoning:

- by entering a select block each resource found this block is initialized to respond to requested events
- select "blocks" with waiteu (if no default case exists) or executes enable events/disable events to just "catch" a single event
- all event detection/status is cleared on all resources used in the select
- process the case statement (effectively servicing the event)
- continue after the select block

this results in configuring the event conditions in every single loop, right?
This all looks correct, yes. Each select disables all events and then enables the required events for the cases contained within it.
There are some possible optimizations if you can guarantee that the state has not been changed by another select, so it should be hoisting some of the setv/setc instructions out of the while loop as long as the resources aren't given as function call parameters inside the select and there isn't a nested select on them or similar. Enabling is still done per-case though, as you can't have other events which were previously set up in another select unexpectedly being jumped to..

The buffering of "events" which you would select on will work for things like inputs from a port or channel as the data is held until it is read or overwritten. However, checks such as pinseq are evaluated at the time that the select case is hit, so you can have sequences such as :

- hit select
- two events happen at once, so one is selected to act upon (call it e1)
- the other event (lets call it e2) uses a pinseq and the pins are equal to the given value
- during processing of e1, the pins change to a different value for e2
- you loop back around and don't hit the event for e2 because the pin values don't match the case requirements

Similar things can happen if you have an input and whilst processing another input arrives, as the buffer is only one input large (iirc; this may depend on the input width, I'm not 100% sure).
ok, but loosing an entire thread just for that could be a little too "expensive"... well, depending on the cost of "missing events" obviously.
If missing events is not a problem then of course this isn't worthwhile, but many/most interfaces can't afford to miss many (any?) events and still be correct.