A question about channels and FIFOs

Technical questions regarding the XTC tools and programming with XMOS.
jarnot
Member++
Posts: 26
Joined: Thu Apr 15, 2010 4:52 pm

A question about channels and FIFOs

Post by jarnot »

I have a project in which one thread is busily gathering a large frame of pretty fast serial data. Once the thread has collected the frame it selects a subset of the data, does some minor manipulations, and ends up with a 24 byte array of data which is passed to another thread which builds up a larger frame of data for transmission via UDP.

In the current setup the first thread is blocked for sufficient time that it 'misses' every second frame synchronisation signal. There is a fair bit of time between the end of one data frame and the start of the next (about 40 ms). Since I currently have threads to spare, the solution that comes to mind is to implement a buffer/FIFO thread which reads data from the data collection thread in a timely manner, and sends it to the next thread for output via UDP. For this to work correctly the buffer/FIFO thread needs to be always 'ready' to accept the 24 byte packets from the one reading the serial data, even when its output channel is not ready.

Rather than inventing an ugly solution, I would like to know if there are examples, recommended approaches or styles to dealing with this kind of problem. I have started by looking in 'Programming XC on XMOS Devices,' and this is where my initial questions arise. In the example about transactions with a master and slave thread, there is a reference to channel buffering. Does anyone know what size a channel buffer is? In the next example on streams there is another reference to channel buffers, and once again it would be helpful to know their size. It is also unclear to me what 'A streaming channel establishes a permanent route between two threads over which data can be efficiently communicated without synchronisation' means by 'without synchronisation.'

All advice is welcome as I do not want to go down a path of writing code in a style which I will regret later.


User avatar
skoe
Experienced Member
Posts: 94
Joined: Tue Apr 27, 2010 10:55 pm

Post by skoe »

Hi, I can't answer all of your questions, but maybe some points:

A normal channel "chan foobar" without transactions does an additional handshake after each piece of data sent through the channel when "foobar <: my_int" is used. This is what the manual calls "synchronized".

A streaming channel "streaming chan foo2" does not do this, but it sends the data in a raw manner. This is much more suitable for high performance applications.

Alternatively transactions could help you. I don't know much about them, only that the handshake is not done after each output statement but only at the synchronization points.

I don't know how large the buffers are, but I assume that they are smaller than what you need.

I'd try to implement a buffer in software. This should be quite simple when you know the frame sizes. Maybe like this (just a quick idea):
1st thread ("bus thread") doesn't collect anything but watches the bus and sends data forward to the 2nd thread imediately.
2nd thread ("buffer thread") collects data, it does only send data to the third thread if it knows that it is free. Maybe the 3rd thread can send a request through a channel to ask the 2nd thread for the next frame. Once the 2nd thread receives this request, it sends a frame to the 3rd one int by int interleaved (by a timer event in a select?) with reading from the 1st one.
3rd thread ("worker thread") can handle data as slow as it needs to be...

The 2nd thread most probably needs two alternating buffers. Maybe you can also find a solution with 2 threads only.

Let us know how you solved it.

Thomas
User avatar
Woody
XCore Addict
Posts: 165
Joined: Wed Feb 10, 2010 2:32 pm

Post by Woody »

When one thread performs a <: output operation on a channel and another thread performs a :> input on the same channel the two threads become synchronized in time at that point. This will often mean that one of the two threads will pause until the time that the other thread reaches the point in it's code where it either reads or write the data.

Streamed channels do not do this: when the :> output occurs, that thread does not pause, but continues executing code, and the data is stored in the receiving channel end's 8 byte input buffer. If that thread then issues another :> output that will also be stored in the input buffer and the code should continue to execute. If a third :> output is issued, the tx thread will pause.

Transactions over an unstreamed channel still synchronize, but do not have much of an overhead to synchronize.

To answer your question directly then:
I would try consider if you can use streamed channels to avoid pausing the threads. If you need to tx more than 8 bytes before the rx thread can start taking the data, you'll need to write the data into a buffer in the shared memory and pass the control information over the channel rather than the data.
Last edited by Woody on Wed May 19, 2010 10:02 am, edited 1 time in total.
User avatar
skoe
Experienced Member
Posts: 94
Joined: Tue Apr 27, 2010 10:55 pm

Post by skoe »

Well, depending on what "pretty fast" means and on how long "minor manipulations" take, my solution may be a bit oversized :)

Woody, I also thought about using shared memory, because this has most probably the highest performance. But afaik it's not possible to use shared memory with pure XC without some inline assembly or C magic. Am I wrong?
Last edited by skoe on Wed May 19, 2010 10:17 am, edited 1 time in total.
User avatar
Woody
XCore Addict
Posts: 165
Joined: Wed Feb 10, 2010 2:32 pm

Post by Woody »

skoe wrote:Woody, does your last hint mean to use shared memory? I also thought about this, because this has most probably the highest performance. But afaik it's not possible to use shared memory with pure XC without some inline assembly or C magic. Am I wrong?
Yes I did mean shared memory (I've edited my post to add the word shared).

You're right that you need to have a C interface to a shared memory. (The XC compiler keeps tight reigns on this to ensure that pure XC code can be written as simply as possible: it throws a thread disjointedness error.)

There is a rather complicated example of a shared memory buffer in classD code at http://www.xmos.com/support/software. Look at fifoRead.c
jarnot
Member++
Posts: 26
Joined: Thu Apr 15, 2010 4:52 pm

Post by jarnot »

Many thanks for the responses. They have filled in a few gaps in my knowledge, and I think that I see a path to a solution. I will let you know what solution I ended up using.
User avatar
lilltroll
XCore Expert
Posts: 956
Joined: Fri Dec 11, 2009 3:53 am
Location: Sweden, Eskilstuna

Post by lilltroll »

There is one pure XC way
use a
const struct

If you use a reference to that struct - you can skip the const in the function header, and perform read/write without the parallel usage error
Probably not the most confused programmer anymore on the XCORE forum.
jarnot
Member++
Posts: 26
Joined: Thu Apr 15, 2010 4:52 pm

Post by jarnot »

Isn't this the kind of craftiness that a future version of the xc compiler is likely to spot and reject?
User avatar
lilltroll
XCore Expert
Posts: 956
Joined: Fri Dec 11, 2009 3:53 am
Location: Sweden, Eskilstuna

Post by lilltroll »

jarnot wrote:Isn't this the kind of craftiness that a future version of the xc compiler is likely to spot and reject?
Probably, thats why I use the tiny font-size
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 »

Dammit Mika you are making my eyes bleed, I had to get my telescope out to read those responses ;-)