Operating System Interest Group for XMOS devices (XOSIG)

Archived group discussions. Please post all group discussions under their relevant XCore group page.
jhrose
Active Member
Posts: 40
Joined: Mon Dec 14, 2009 11:18 am

Post by jhrose »

Hei,
...playing about with channel ends...
This looks much better than before with typedef chanend and I think a compiler would stand a better chance of type-checking the protocol. Though in motor_controller you persist using the select statement with the control port in two case statements. This resembles PiXC using protocol cases in select statements for channel inputting. But PiXC doesn't describe the use of ports, and it breaks a fundamental in XC: each port and timer may appear in only one of the select case statements. This is because the XMOS architecture restricts each port and timer resource to waiting for just one condition at a time. So, is there a concept you want to express that I'm missing?

As before I tried an almost-XC-compileable (that gets similar compiler complaints about the chanend resource type) by fixing the select statement, and I also assume your channel broadcast pattern would be implemented using a process since channels are point-to-point resources in the XMOS architecture. And I've optimised use of stack memory a bit further, by receiving into mc member variables:

Code: Select all

/*
 * main.xc - project typedef_chanend
 *
 * 26.june.2010
 *
 * www.xcore.com, forum:Group Discussion, author:Folknology
 *
 * The idea is a variation on the protocol, to 'type' channel endpoints using
 *  C unions, structures and typedef
 */

# include <platform.h>
# include <stdio.h>

typedef enum
{
    SYNC
} channel_pattern;

{ chanend, chanend } new_channel( channel_pattern const );


typedef enum
{
    START,
    STOP
} Signal;

typedef struct {
  Signal sig;
  unsigned char msg[12];
} Message;

typedef struct {
  chanend rx;
  chanend tx;
  union {
    Message M;
    Signal S;
  } protocol;
} Messenger;


void motor_controller( Messenger mc, in port control ) {
  Message motorStart = { START, "Starting   " };
  Message motorRestart = { START, "Restarting " };
  int inp;

  mc.tx <: motorStart;
  while(1) {
    control when pinsneq( 0x3 ):> inp;
    {
      if( 0x1 & inp ) {
          mc.tx <: STOP;
      }
      else
      if( 0x2 & inp ) {
          mc.tx <: motorRestart;
      }
    }
  }
}

void motor_driver( Messenger mc, out port motor ) {
  while(1) {
  select
    {
      case mc.rx :> mc.protocol :
      {
        switch( mc.protocol.S )
        {
          case START :
            printf("Motor %s",mc.protocol.M.msg);
            motor <: START;
            break;

          case STOP :
            printf("Motor shutdown");
            motor <: STOP;
            break;
        }
      }
      break;
    }
  }
}

in port control = XS1_PORT_1K;
out port motor = XS1_PORT_1L;

int main ( void ) {
  Messenger mc;
  {mc.tx, mc.rx} = new_channel(SYNC);
  par {
    motor_controller(mc,control);
    motor_driver(mc,motor);
  }

  return( 0 );
}
And I tried a second almost-XC-compileable using transactions to wrap up the protocol exchanges:

Code: Select all

/*
 * main.xc - project typedef_chanend with transaction
 *
 * 26.june.2010
 *
 * www.xcore.com, forum:Group Discussion, author:Folknology
 *
 * The idea is a variation on the protocol, to 'type' channel endpoints using
 *  C unions, structures and typedef.
 *
 *
 * A transaction consists of a master thread and a slave thread running
 *   concurrently. It is used for an exchange of data between the two
 *   parallel threads.
 *
 * These master and slave keywords facilitate an asynchronous coupling,
 *   so that the master is not blocked when sending (up to the buffering
 *   capacity of a channel) if the slave is not ready to receive.
 *
 * Each transaction is permitted to communicate on precisely one channel.
 *   This ensures deadlocks do not arise due to an output on one channel
 *   blocking as a result of a switch being full with incoming data that
 *   is not yet ready to be received.
 *
 */

# include <platform.h>
# include <stdio.h>

typedef enum
{
    SYNC
} channel_pattern;

{ chanend, chanend } new_channel( channel_pattern const );


typedef enum
{
    START,
    STOP
} Signal;

typedef struct {
  Signal sig;
  unsigned char msg[12];
} Message;

typedef union {
    Message M;
    Signal S;
} protocol;

typedef struct {
  chanend rx;
  chanend tx;
  protocol p;
} Messenger;


transaction outProtocol( chanend tx, Message msg )
{
  switch( msg.sig )
  {
    case STOP :
    {
      tx <: msg.sig;
    }
    break;

    case START :
    {
      tx <: msg;
    }
    break;
  }
}

void motor_controller( Messenger mc, in port control ) {
  Message motorStart = { START, "Starting   " };
  Message motorRestart = { START, "Restarting " };
  Message stop = { STOP, "           " };
  int inp;

  mc.tx <: motorStart;
  while(1) {
    control when pinsneq( 0x3 ):> inp;
    {
      if( 0x1 & inp ) {
          master outProtocol( mc.tx, stop );
      }
      else
      if( 0x2 & inp ) {
          master outProtocol( mc.tx, motorRestart );
      }
    }
  }
}

transaction inProtocol( chanend rx, protocol p )
{
  rx :> p.S;

  switch( p.S )
  {
    case START :
    {
      rx :> p.M;
    }
    break;

    case STOP :
    {
    }
    break;
  }
}

void motor_driver( Messenger mc, out port motor ) {
  while(1) {
  select
    {
      case slave { inProtocol( mc.rx, mc.p ); } :
      {
        switch( mc.p.S )
        {
          case START :
            printf("Motor %s",mc.p.M.msg);
            motor <: START;
            break;

          case STOP :
            printf("Motor shutdown");
            motor <: STOP;
            break;
        }
      }
      break;
    }
  }
}

in port control = XS1_PORT_1K;
out port motor = XS1_PORT_1L;

int main ( void ) {
  Messenger mc;
  {mc.tx, mc.rx} = new_channel(SYNC);
  par {
    motor_controller(mc,control);
    motor_driver(mc,motor);
  }

  return( 0 );
}
User avatar
Folknology
XCore Legend
Posts: 1274
Joined: Thu Dec 10, 2009 10:20 pm

Post by Folknology »

Actually this could clean up a little if we used a protocol.h to start hiding some complications :

Code: Select all

/*
* main.xc - project typedef_chanend
*
* 26.june.2010
*
* www.xcore.com, forum:Group Discussion, author:Folknology
*
* The idea is a variation on the protocol, to 'type' channel endpoints using
*  C unions, structures and typedef
*/

# include <platform.h>
# include <stdio.h>

/* Header for protocols.h */
#define protocols union

typedef struct {
  chanend rx;
  chanend tx;
} Channel;

typedef enum
{
    SYNC
} channel_pattern;

channel new_channel( channel_pattern const );

/*  end header */

typedef enum
{
    START,
    STOP
} Signal;

typedef struct {
  Signal sig;
  unsigned char msg[12];
} Message;

protocols {
  Message M;
  Signal S;
} messageProtocol;

struct {
  Channel channel;
  messageProtocol protocol;
} Messenger;

void motor_controller( Messenger mc, in port control ) {
  Message motorStart = { START, "Starting   " };
  Message motorRestart = { START, "Restarting " };
  int inp;

  mc.channel.tx <: motorStart;
  while(1) {
    control when pinsneq( 0x3 ):> inp;
    {
      if( 0x1 & inp ) {
          mc.channel.tx <: STOP;
      }
      else
      if( 0x2 & inp ) {
          mc.channel.tx <: motorRestart;
      }
    }
  }
}

void motor_driver( Messenger mc, out port motor ) {
  while(1) {
  select
    {
      case mc.channel.rx :> mc.protocol :
      {
        switch( mc.protocol.S )
        {
          case START :
            printf("Motor %s",mc.protocol.M.msg);
            motor <: START;
            break;

          case STOP :
            printf("Motor shutdown");
            motor <: STOP;
            break;
        }
      }
      break;
    }
  }
}

in port control = XS1_PORT_1K;
out port motor = XS1_PORT_1L;

int main ( void ) {
  Messenger mc = {new_channel(SYNC); messageProtocol};
  par {
    motor_controller(mc,control);
    motor_driver(mc,motor);
  }

  return( 0 );
}
This also allows protocols and and channels to be defined and reused separately. Protocols would often be used with different channels I would imagine.
jhrose
Active Member
Posts: 40
Joined: Mon Dec 14, 2009 11:18 am

Post by jhrose »

Hei,

That looks fine (as a non-XC-compilable).

Further to your forum posting on channel ends http://www.xcore.com/forum/viewtopic.ph ... 55&start=0, I've attached an XC-compilable based on your ideas and using transactions for performing 'protocol-chanend' i/o that reduces the potential source of errors from having i/o code inlined in each user function. This method fails to solve 'chanend of type p', but is a half-decent substitute using XC and allows protocols and and channels to be defined and reused separately.

Code: Select all

/*
 * main.xc - project typedef_chanend with transaction
 *
 * 27.june.2010
 *
 * www.xcore.com, forum:Group Discussion, author:Folknology
 *
 * The idea is a variation on the protocol, to relate channel endpoints with
 *  a protocol type of C unions, structures and typedef. And to exchange protocol
 *  data using transaction functions, as a safer method than inline input/output
 *  code.
 */

# include <platform.h>
# include <stdio.h>
# include "protocol.h"


transaction outProtocol( chanend tx, protocol p )
{
  switch( p.M.sig )
  {
    case STOP :
    {
      tx <: p.S;
    }
    break;

    case START :
    {
      tx <: p.M;
    }
    break;
  }
}

void motor_controller( chanend pTx, protocol p, in port control ) {
  protocol motorStart = {{ START, "Starting   " }};
  protocol motorRestart = {{ START, "Restarting " }};
  protocol stop = {{ STOP, "           " }};
  int inp;

  pTx <: motorStart;
  while(1) {
    control when pinsneq( 0x3 ):> inp;
    {
      if( 0x1 & inp ) {
          master outProtocol( pTx, stop );
      }
      else
      if( 0x2 & inp ) {
          master outProtocol( pTx, motorRestart );
      }
    }
  }
}

transaction inProtocol( chanend rx, protocol p )
{
  rx :> p.S;

  switch( p.S )
  {
    case START :
    {
      rx :> p.M;
    }
    break;

    case STOP :
    {
    }
    break;
  }
}

void motor_driver( chanend pRx, protocol p, out port motor ) {
  while(1) {
  select
    {
      case slave { inProtocol( pRx, p ); } :
      {
        switch( p.S )
        {
          case START :
            printf("Motor %s",p.M.msg);
            motor <: START;
            break;

          case STOP :
            printf("Motor shutdown");
            motor <: STOP;
            break;
        }
      }
      break;
    }
  }
}

in port control = XS1_PORT_1K;
out port motor = XS1_PORT_1L;

int main ( void ) {
  protocol mcP;
  chan mc;

  par {
    motor_controller(mc,mcP,control);
    motor_driver(mc,mcP,motor);
  }

  return( 0 );
}

Code: Select all

/*
 * protocol.h
 *
 *  Created on: 27 Jun 2010
 *      Author: Julian
 */

#ifndef PROTOCOL_H_
#define PROTOCOL_H_

typedef enum
{
    START,
    STOP
} Signal;

typedef struct {
  Signal sig;
  unsigned char msg[12];
} Message;

typedef union {
    Message M;
    Signal S;
} protocol;


#endif /* PROTOCOL_H_ */
User avatar
Folknology
XCore Legend
Posts: 1274
Joined: Thu Dec 10, 2009 10:20 pm

Post by Folknology »

Thanks Julian

The transaction based approach gives me some more ideas to think over.

In my previous example the protocols.h was generically for any protocols rather than a specific protocol as used in this XC compatible response.

If I follow your example it could work with specific defined protocol examples. this could happen with modules of a library for example where there was a'.h' and '.xc' compliment. e.g. 'motor.h" and "motor.xc", this approach is definitely worth consideration.

Not sure this would be forward compatible or not with 'real' protocols, but obviously that would be a good idea.

Time for some more thought..

*Update Using this technique could be referred to as using protocol transactors

*Update 2 There is also an issue with using multiple protocols in a single file, if for example I had a motor_lamp_controller that operated both a motor using motor protocol and a lamp using lamp protocol then the union->protocol naming would collide. Question is how does one solve that generally, obviously reusing protocols as much as possible is beneficial but there are bound to be cases when a module might use more than one protocol.