Difference between syntactic variants? (channels)

New to XMOS and XCore? Get started here.
Treczoks
Active Member
Posts: 38
Joined: Thu Mar 21, 2013 11:18 am

Difference between syntactic variants? (channels)

Post by Treczoks »

Hi!

(I hope I'm not getting on too many nerves with my nearly continous posting, but I still have more questions than answers regarding XMOS. Sorry for that. HINT: Proper documentation might have answered a lot of questions...)

I'm still trying to grok channels and started some more-or-less systematic experiments to learn where the documentation lacks. I would not mind collecting my findings into a document for public use.

Now I've got questions for the initiated:
Assuming that C is a chan, what is the difference between "C <: 1;" and "inuint(C,1);", respectively "C :> MyVar;" and "MyVar = outuint(C);"?
Why is there an inuint() and outuint(), but no sinuint() and soutuint()?

Yours, Christian Treczoks


User avatar
segher
XCore Expert
Posts: 844
Joined: Sun Jul 11, 2010 1:31 am

Post by segher »

Treczoks wrote:HINT: Proper documentation might have answered a lot of questions...)
There almost certainly is a lot of documentation that you haven't read.
It is hard to find nowadays on the website though :-(

If you have suggestions on how to organise the documentation better,
I'm sure XMOS would love to hear it.
Assuming that C is a chan, what is the difference between "C <: 1;" and "inuint(C,1);", respectively "C :> MyVar;" and "MyVar = outuint(C);"?
[You swapped inuint and outuint.]
On a normal channel, the input/output operators do a handshake
between the two channel ends before and after sending the data;
inuint() and outuint() do not.
Why is there an inuint() and outuint(), but no sinuint() and soutuint()?
Those functions would do the same as the normal I/O operators,
on a streaming channel.
User avatar
Ross
XCore Expert
Posts: 966
Joined: Thu Dec 10, 2009 9:20 pm
Location: Bristol, UK

Post by Ross »

In the "early days" there were no such thing as "streaming" channels or "transactions".

To get similar functionality as a streaming channel you declared a normal channel and used the outuint(), intuint() etc functions.

You would also use start_streaming_master(), stop_streaming_master() before/after using the channel.

(actually the stop was the important one since you need to close down the route before you free the channel resource)

They are still there for historical reasons and advanced usage.

So the previous:

Code: Select all

master(chanend c)
{
   start_streaming_master(c);

   while(core_active)
   {
      // do your comms
      x= inuint(c);
      outuint(c, x);
   }

   // time to terminate
   stop_streaming_master(c)
      
}
becomes

Code: Select all

master(streaming chanend c)
{
   while(core_active)
   {
      // do your comms
       c :> x;
       c <: x;
   }
}
The compiler does the start/stop handshake for you.
Treczoks
Active Member
Posts: 38
Joined: Thu Mar 21, 2013 11:18 am

Post by Treczoks »

segher wrote:There almost certainly is a lot of documentation that you haven't read.It is hard to find nowadays on the website though :-(
I agree on both accounts. Any hints like document IDs I should hunt for? I'm totally amazed that I could find NOTHING about advanced channel usage beside the assember manual and the library reference.
And I'm sure there is a lot of documentation I have not read because they don't have it. A system is always different when you only see it from the inside than from the outside, and so is the documentation done by people on the "inside" for people on the "outside". Take all this Eclipse stuff, for example. If you are doing embedded stuff, you usually use Keil or iSystem or similar specialized IDEs. Eclipse and gdb is a bad choice for amything that lacks an OS (IMHO). So despite years of experience in the field, this is my first time with Eclipse. And some knowledge about Eclipse (or their implementation/variation of it) is taken for granted by XMOS - things that made me pull my hair as I just could not find the settings I was looking for.
segher wrote:If you have suggestions on how to organise the documentation better,I'm sure XMOS would love to hear it.
I sent some suggestions already, explaining them the situation from my viewpoint as a beginner with XMOS, and they do listen. Problem is that I cannot tell them where to put the "Advanced channel usage" manual, as I have not found one so far. All I can ask is if they could provide one.
segher wrote:
Assuming that C is a chan, what is the difference between "C <: 1;" and "inuint(C,1);", respectively "C :> MyVar;" and "MyVar = outuint(C);"?
[You swapped inuint and outuint.]
Ooops. Yes, you are right. Thanks for catching this.
segher wrote:On a normal channel, the input/output operators do a handshake between the two channel ends before and after sending the data; inuint() and outuint() do not.
What kind of handshake do you refer to? In my test project I could communicate between two threads either by "C <: 1;" and "C :> MyVar;" or by "outuint(C,1);" and "MyVar = inuint(C);" (I have not tried to "cross the streams", though). For neither variant, an additional handshake was necessary.
segher wrote:
Why is there an inuint() and outuint(), but no sinuint() and soutuint()?
Those functions would do the same as the normal I/O operators,on a streaming channel.
Hmm, the mysterious handshake strikes again... Looks like it is about time to investigate this mystery with a disassembler (or grab some XMOS employees and tickle-torture them until we get documentation :-) ).

Yours, Christian Treczoks
Treczoks
Active Member
Posts: 38
Joined: Thu Mar 21, 2013 11:18 am

Post by Treczoks »

Hi!

My channel experiments continue, and I've seen something totally odd I can't explain.

In the attached archive I've got two sources, one with streaming channels and ":>" syntax, and one with normal channels and "inuint()/outuint()" syntax. Both pass the same data and control tokens from F1() to F2().

From what I learned here, a streaming channel should be set up at start and closed at the end, while with a normal channel, the opening and closing should be made with every access.

But the disassembly shows that they are both nearly identical except that a) the streaming channel version exchanges some CT_END tokens at the end before closing down the channel, and b) some addresses are off by four to account for the four additional commands im main().

Using the ":>" syntax in the non-streaming version does not work (as I've already expected, I'm grabbing command tokens of the channel which get passed from "<:" to ":>", giving me a "Token: 1" for the CT_END probably send by "<:").

But I honestly did not expect the non-streaming variant to work at all, as a channel is neither routed by marking the channel as streaming, nor does it use the "<:".

What is happening here?

Yours, Christian Treczoks
You do not have the required permissions to view the files attached to this post.
User avatar
Bianco
XCore Expert
Posts: 754
Joined: Thu Dec 10, 2009 6:56 pm

Post by Bianco »

Synchronisation is documented here:

http://www.xmos.com/published/abi32 chapter 10. I do believe that this document isn't available anymore directly from the website (this one is actually pretty old, but everything is valid)
richard
Respected Member
Posts: 318
Joined: Tue Dec 15, 2009 12:46 am

Post by richard »

Bianco wrote:http://www.xmos.com/published/abi32 chapter 10. I do believe that this document isn't available anymore directly from the website (this one is actually pretty old, but everything is valid)
The ABI has been incorporated into the "Tools Development Guide" document available here:

https://www.xmos.com/support/documentat ... egory=Misc
User avatar
Ross
XCore Expert
Posts: 966
Joined: Thu Dec 10, 2009 9:20 pm
Location: Bristol, UK

Post by Ross »

Treczoks wrote:Hi!

From what I learned here, a streaming channel should be set up at start and closed at the end, while with a normal channel, the opening and closing should be made with every access.

But the disassembly shows that they are both nearly identical except that a) the streaming channel version exchanges some CT_END tokens at the end before closing down the channel, and b) some addresses are off by four to account for the four additional commands im main().
This is exactly as expected - essentially you are emulating a streaming channel using the outuint() and inuint() functions.
Treczoks
Active Member
Posts: 38
Joined: Thu Mar 21, 2013 11:18 am

Post by Treczoks »

Ross wrote:This is exactly as expected - essentially you are emulating a streaming channel using the outuint() and inuint() functions.
Hmm, now I'm REALLY confused.
I can have either a normal channel or a streaming channel.
I thought that if I had a streaming channel, it would be set up at the entry of the function (or execution block) and closed at the end of it, and can be used without any additional adressing and routing functions within the block. And if I had a normal channel, it would have to be "opened" and "closed" for every transaction.
Am I right so far, or am I totally off?
But here, both disassemblies are more or less identical, despite one source using channels and one using streaming channels.
And what do you mean by "emulating a streaming channel" by using outuint() and inuint()?
The only thing that would make sense in this context would be that the compiler sees me using outuint() and inuint() and decides to promote the channel I defined to a streaming channel. If the compiler does things like that, then they should definitely be documented prominently.

Yours, Christian Treczoks
User avatar
segher
XCore Expert
Posts: 844
Joined: Sun Jul 11, 2010 1:31 am

Post by segher »

Treczoks wrote:And if I had a normal channel, it would have to be "opened" and "closed" for every transaction.
Am I right so far, or am I totally off?
It depends what you mean by "opened" and "closed".

To set up a channel, each side does a GETR instruction to get a
new channel end, and does a SETD on it with the channel end id
of the other side, to say what destination channel end should be
addressed.

Then, for every transaction, both sides do a handshake around
the transaction: that involves sending (and checking for) END
tokens. When switches along the way see an END token, they
free the resources they dedicated to this data stream (like, the
outgoing link they used), to give other channels a chance to use
it. For any data sent on a channel that is not "open" in this way,
the originating channel end sends a header, the switches look at
the header, decide how to route it, reserve the needed resources.

A streaming channel does not do these handshakes, so it has somewhat
better bandwidth and much better guaranteed latency. There are
of course also downsides to it: a streaming channel holds up all the
links it uses between the channel ends for its private use, which is
rather costly.

So, the only difference at the machine code level between streaming
and non-streaming channels is that normal channels put the handshake
around transactions. If you don't use the in/out operators but instead
call inuint()/outuint() you do not get the handshake on a normal channel
either.

[And for a streaming channel an END is sent when it is closed; on a normal
channel you don't have to, the last handshake already sent it].

There is nothing magic about streaming channels. For the hardware,
they are the *same thing* as any other channel. The generated code
does not do transactions; that is all.