XC 3.4 Transactions

Archived XCore tutorials.
User avatar
Folknology
XCore Legend
Posts: 1274
Joined: Thu Dec 10, 2009 10:20 pm

XC 3.4 Transactions

Post by Folknology »

Unfortunately Programming XC on XMOS Devices - section 3.4 seems FUBB, here are the examples for section:

This first example looks ok:

Code: Select all

# include < platform .h >
int snd [3] , rcv [3];
int main ( void ) {
   chan c ;
   par {
     on stdcore [0] : master {      // Thread X
       for ( int i =0; i <10; i ++)
          c <: snd [ i ];
     }
     on stdcore [1] : slave {       // Thread Y
       for ( int i =0; i <10; i ++)
          c : > rcv [ i ];
} } }
The second attempts to show how transactors work but is just wrong in so many ways :cry:

Code: Select all

transaction inArray ( chanend c , int data [] , int size ) {
  for ( int i =0; i < size ; i ++)
    c : > data [ i ];
}
int main ( void ) {
  chan c ;
  int snd [3] , rcv [3];
  par {
    master inArray (c , rcv , 3);
    slave {
      for ( int i =0; i <10; i ++)
         c : > rcv [ i ];
} } }

For the third example I have absolutely no idea what the frigg is going on, help me, please make it stop :?

Code: Select all

select {
  case slave { inArray ( c1 , packet , P_SIZE ); } :
    process ( packet );
    break ;
  case slave { inArray ( c2 , packet , P_SIZE ); } :
    process ( packet );
    break ;
}
Lets try to clear this up so newbies at least have a fighting chance at learning XC instead of bouncing of the walls..
Last edited by Folknology on Thu May 27, 2010 12:25 am, edited 1 time in total.
User avatar
Folknology
XCore Legend
Posts: 1274
Joined: Thu Dec 10, 2009 10:20 pm

Post by Folknology »

So assuming example 1 is fine, it looks ok and makes sense lets have a go at cracking example 2
  • 1. The name inArray is a poor name for the transactor IMHO, how about rxIntArray instead.
  • 2. Both master and slave appear to be receiving, that's never going to work so that needs fixing!
  • 3. Lets make use of the unused snd[] int array to create a sender in the slave to complement the transactor receiver (I am assuming it is what this is for).
  • 4. Lets also use the real size of the int arrays 3 for the for loop, rather than a figure just plucked out of the air like 10
Obviously this still isn't perfect compared to example 1 as sender (slave) and receiver (master) roles have switched and aren't consistent for the reader. On the otherhand this illustrates that neither has to be one or the other as long as they compliment each other.

So here is my attempt to fix it and it does compile and run on my XK-1

Code: Select all

# include <platform.h>

transaction rxIntArray (chanend c,int data [],int size) {
  for (int i =0;i<size;i++)
    c :> data[i];
}

int main (void) {
  chan c ;
  int snd[3], rcv[3];
  par {
    master rxIntArray (c,rcv,3);
    slave {
      for (int i=0;i <3;i ++)
        c <: snd[i];
     }
  }
}
User avatar
Folknology
XCore Legend
Posts: 1274
Joined: Thu Dec 10, 2009 10:20 pm

Post by Folknology »

On the third example, I am rather lost as to an explanation and am hoping for some help here.

But I do notice the following :
  • 1. This example appears to be plucked from some random code, it provides zero context for us to understand it
  • 2. Where would this code reside; in Main in a par block?
  • 3. Why does the in data[] array suddenly changed to 'packet' compared to previous examples?
  • 4. Why has size been swapped for P_SIZE, not exactly consistent.
  • 5. What is process meant todo is it receiving or transmitting?
  • 6. What is the purpose of this code and what is it actually showing us?
Suggestions, ideas or translations?
dougxmos
Junior Member
Posts: 6
Joined: Thu May 27, 2010 10:13 am

Post by dougxmos »

The errata for the book appears not to have made it into the PDF yet, we'll get that fixed soon.

https://www.xmos.com/products/documenta ... ta/english

Regarding the final example, it's not specifically related to the previous examples. It's just showing that you can use a slave transaction in the guard of a select statement. The transaction provides a way to communicate a block of data efficiently, without having to synchronize on the communication of each individual word, and it's possible to select upon the start of this communication.
User avatar
Folknology
XCore Legend
Posts: 1274
Joined: Thu Dec 10, 2009 10:20 pm

Post by Folknology »

Thanks for the reply Doug, appreciate you time
The errata for the book appears not to have made it into the PDF yet, we'll get that fixed soon.
That would be good as it tends to be used by newbies as their first point of contact for XC. Any ideas on timescales?
Thanks for the link to the errata, I didn't know about this.

One idea that is commonly used in open source documentation is the publishing of the sections into a wiki, to allow either direct manipulation, corrections or enhancements, or alternatively comments underneath for the same purposes. Depending on what the source of the book is wriitten using latex or something perhaps, one can easily export to textile or html pages. That way the community can help fix and errors and or suggest improvements. It also provides an up to date online reference for queries. Here is an example I have used before that's really handy - Postgrsql Manuals using the comment method.
Regarding the final example, it's not specifically related to the previous examples
Well first it should be where possible in order to be consistent. Also your appears to try to be because it uses the same transactor 'inArray' so I'm confused by this response.

I am also still confused by its lack of context and purpose, does process send or receive what is the function? I would really like to understand this select form of the transaction, can your please provide an explanation and perhaps show it in a program/function.

regards
Al
dougxmos
Junior Member
Posts: 6
Joined: Thu May 27, 2010 10:13 am

Post by dougxmos »

Let's say you have a consumer thread that processes data from two producer threads. In this case we'll assume that one of these producers sends words of data at a time, and the other sends packets of data each containing n words. To transmit the packet efficiently you define

Code: Select all

transaction outArr(chanend c, char data[], int n) { ... }
transaction inArr(chanend c, char data[], int n) { ... }
Then in one thread you out words to a channel:

Code: Select all

// T1
while (1) {
  ...
  c <: produced data;
}
In another thread you out packets to a channel:

Code: Select all

// T2
while (1) {
  ...
  master outArr(d, packet, n);
}
In the third thread you process packets from both threads:

Code: Select all

// T3
while (1) {
  select {
    case c :> x :
      proecess x
      break;
    case slave { inArray(d, packet, n); } :
      process packet;
      break;
  }
}
Hope this helps clarify.
User avatar
Folknology
XCore Legend
Posts: 1274
Joined: Thu Dec 10, 2009 10:20 pm

Post by Folknology »

Thanks Doug

I think I get it but am letting it sink in and trying to think how I would use it.

Clearly the third example does not help and something more like this one helps a great deal.

But I also think there is a weakness in the XC programming text generally around the select construct.

Maybe the select construct should be covered before even the I/O section in a more generic fashion, but its a catch 22 situation how do you show select usage without IO, Timers or Channels?

Given the importance and the complexity of the select construct and more advanced uses of it such as select functions and replication it almost needs its own dedicated chapter. That could mean leaving out selects on IO and Channel intros and then introducing it afterwards in its own scope perhaps. I certainly feel it is an important enough construct to consider within its own right.

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

Post by lilltroll »

In the XMOS module SRAM ver. 1.1 you can find this example:

Code: Select all

/** This get a Cmd data structure in a transaction, to make things faster.
 */
transaction getCmd(chanend c, unsigned char &Cmd, unsigned int &Adrs, int &Count)
{
   c :> Cmd;
   c :> Adrs;
   c :> Count;
}
You can also find rows like this:

Code: Select all

      // generate tranfser
      master
			{
         c_sram <: (unsigned char) SRAM_READ_CMD;
         c_sram <: (unsigned int)  StartAdrs;
         c_sram <: (int) count;
      }
      c_sram :> Resp;
What I doesn't understand is how the getCmd just can be stand alone. I cannot find any calls to the getCmd.

I did expect something like:

Code: Select all

transaction getCmd(chanend c, int &D, int &E, int &F)
{
   c :> D;
   c :> E;
   c :> F;
}


int main(){

	chan c;
	int D,E,F;
	const int A=1,B=2,C=3;
	par{
	slave getCmd(c,D,E,F);
	master{
	    c <: A;
	    c <: B;
	    c <: C;
		}
	}
	while(1);
	return 0;
}
(With this example I'm able to do 3 out instructions without any outct instructions in between)
Probably not the most confused programmer anymore on the XCORE forum.
User avatar
Folknology
XCore Legend
Posts: 1274
Joined: Thu Dec 10, 2009 10:20 pm

Post by Folknology »

Hi Mika

As a general comment looking through the source code I wonder if this example should be published as is in the Xmos software reference section, it's an exceptionally sloppy piece of work even by my poor standards! It really needs a clean up, Don't get me wrong I like to see examples of how things are done, but this kind of stuff belongs on a wiki until it's been polished and peer reviewed!
What I doesn't understand is how the getCmd just can be stand alone. I cannot find any calls to the getCmd.
To answer your question I think they re-factored the code without cleaning up and that function should be either commented out or better still removed. Instead I think they used :

Code: Select all

#define SLAVE_CASE(enabled, sram_chan, Cmd, Adrs, Count) case enabled => slave { sram_chan :> Cmd; sram_chan :> Adrs; sram_chan :> Count; }:
in the following

Code: Select all

select
      {
#define SLAVE_CASE(enabled, sram_chan, Cmd, Adrs, Count)
case enabled => slave { sram_chan :> Cmd; sram_chan :> Adrs; sram_chan :> Count; }:
         SLAVE_CASE(chanEnabled[0], port1, Cmd, Adrs, Count);
               serverActive = SRAM_SvrCmdExec(port1, Cmd, Adrs, Count);
               break;

         SLAVE_CASE(chanEnabled[1], port2, Cmd, Adrs, Count);
               serverActive = SRAM_SvrCmdExec(port2, Cmd, Adrs, Count);
               break;

         SLAVE_CASE(chanEnabled[2], port3, Cmd, Adrs, Count);
               serverActive = SRAM_SvrCmdExec(port3, Cmd, Adrs, Count);
               break;
}
But it's fairly sloppy and difficult to read especially with the commented out stuff in the middle (which I removed for clarity)!!
(With this example I'm able to do 3 out instructions without any outct instructions in between)
by combining the 3 channel outputs into a slave transaction the synchronisation happens after all 3 have executed, without that the processing would pause on each output as it synchronised. Thus the transactional (master/slave) arrangement provides more efficient and timely transfers.

Actually I would like to see XC support for protocols alongside channels, these could include automatic transactional channel usage in exactly this kind of situation. What's more it promotes better type safety, simpler semantics and more sophisticated communication patterns for inter-process communication.
User avatar
lilltroll
XCore Expert
Posts: 956
Joined: Fri Dec 11, 2009 3:53 am
Location: Sweden, Eskilstuna

Post by lilltroll »

OKi thanks, I was hoping that it just was some old junk in the code, since I couldn't track it in the debug or XTA. But I wasn't sure if it was some kind of a magic code tric :mrgreen:

What about the XC libs.

You can find:

transaction in_char_array ( chanend c,
char src[],
unsigned size
)


Input a block of data from a channel.

A total of size bytes of data are input on the channel end and stored in an array. The call to in_char_array() must be matched with a call to out_char_array() on the other end of the channel. The number of bytes input must match the number of bytes output.

Parameters:
c The channel end to input on.
src The array to store the values input from on the channel.
size The number of bytes to input.

AND

transaction out_char_array ( chanend c,
const char src[],
unsigned size
)


Output a block of data over a channel.

A total of size bytes of data are output on the channel end. The call to out_char_array() must be matched with a call to in_char_array() on the other end of the channel. The number of bytes output must match the number of bytes input.

Parameters:
c The channel end to output on.
src The array of values to send.
size The number of bytes to output.

Does that act/compile to exactly the same thing or to something "similar", or is it just an old way to write things for compatibility ?

(Hmmm, If you use an old function in MATLAB that isn't recommended to be used for "new code" you get a warning)
Probably not the most confused programmer anymore on the XCORE forum.