Questions using select statements and channel usage

Technical questions regarding the XTC tools and programming with XMOS.
Post Reply
User avatar
landon
Experienced Member
Posts: 71
Joined: Mon Sep 06, 2010 4:05 pm
Contact:

Questions using select statements and channel usage

Post by landon »

I'm trying to figure out the best way to structure some code for a controller which has typical needs such as:
  • Rx/Tx UART access
    ADC acquisition via SPI
    Storing samples in a FIFO
    Supplying a console user interface to configure the device
I've written each of these pieces and tested them standalone except the console which will start gluing things together, but I'm a little stumped with structuring it for integration and communication amongst pieces.

I think my best bet is to create a "controller" thread that uses channels to communicate with threads that take care of these other tasks and can implement the executive/console portion.

I want to be able to write a controller which can implement the command console by interacting with the rx and tx threads, pulling information as directed from the FIFO or acquisition threads, configuring things like the sample rate in the acquisition thread by sending configuration commands to it via the channel.

If there is receive data, I need to be able to respond to it almost immediately and potentially interrupt the transmission of other data in the transmit thread. It seemed like a select statement would be a natural choice, but I'm finding that if there's a lot of channel data for one of the select cases, the others never get a chance to run. Seems this select channel structure will either block or starve others.

I'm looking for suggestions or examples on how to best structure code to integrate tasks like this.

Here's the skeleton of what I have (minus implementation specific code ) - just the basic parallelism and channel arrangement I've been experimenting with so you get the idea of what I'm trying to do.

Code: Select all

/* channel-test.xc
 *   experiment with how channels work
 *   attempt to create a master controller which communicates via channels
 *   to all other threads
 */

#include <xs1.h>
#include <print.h>
#include <platform.h>
#include <xclib.h>
#include <string.h>
#include "xfifo.h"

#define FIFO_SIZE  (11 * 1024)

void acquire(chanend controllerChan )
{
	while( 1 )
	{
		controllerChan <: 5;  // dummy data
		printstrln("Acquire putting 5 into acq channel");
	}

}

void rx(chanend controllerChan )
{
	while( 1 )
	{
		//controllerChan <: (int)'a';  // dummy data
		//printstrln("Rx Putting 'a' into receive channel");
	}
}

void tx(chanend controllerChan )
{
	unsigned int txData;

	while( 1 )
	{
		controllerChan <: txData ;  // get data from channel to send out
		printstrln("Tx Got data to transmit");
	}

}

void monitor_fifo(chanend controllerChan )
{
	while( 1 )
	{
		controllerChan <: 1;    // dummy
		printstrln("Fifo monitor Put 1 into fifo control channel");
	}

}

void controller(chanend rxChan, chanend txChan, chanend acqChan, chanend fifoChan )
{
	unsigned int rxData, txData, acqData, fifoData;

	txData = 100;

	while( 1 )
	{
		select {
		case rxChan :> rxData :
			// do something with rxData
			printstrln("Rx Data received");
			break;
		case acqChan :> acqData :
			// do something related to data acquired
			printstrln("Acq Data received");
			break;
		case fifoChan :> fifoData :
			// do something with fifoData received
			printstrln("Fifo Data received");
			break;
		}

		// default
		if ( txData != 0x00 )
		{
			printstrln( "Putting 100 into Tx channel");
			txChan :> txData;
			// change some status related to tx data
			printstrln( "Put 100 into Tx channel");
		}
	}
}

unsigned int fifobuf[FIFO_OVERHEAD + FIFO_SIZE];

int main()
{
	chan rxChan, txChan, acqChan, fifoChan;

	xfifo_t fifo;
	fifo = xfifo_init( fifobuf, FIFO_SIZE);

	par {
		controller( rxChan, txChan, acqChan, fifoChan );
		monitor_fifo(fifoChan);
		acquire(acqChan);
		rx(rxChan);
		tx(txChan);
	}

	return 0;  // should never get here
}
What are some typical patterns people use to accomplish this type of thing? That is, an executive console that controls operation and data flow between various threads.

I hope this isn't too general a question, but if so, I'd like to focus on how to create an executive loop that can check multiple channels data/status without getting blocked - would be nice to see something like a channel peek() - I could be missing something key.

Thanks for any tips,

Landon


User avatar
bsmithyman
Experienced Member
Posts: 126
Joined: Fri Feb 12, 2010 10:31 pm
Contact:

Post by bsmithyman »

I've been wondering about something very similar, though I've been working on understanding transactions to speed things up, possibly aggregated on a spare thread per core for transmission through the G4 switch.

I've got a fairly long-term (read: underfunded and sporadic) robot project embedding an XC-1(A) with a Beagleboard running Linux. I'd like to have a unified signalling system between the XMOS board and the Linux host. My intention is to set up a thread or two as dispatchers on the XMOS board running the embedded processes and sending somewhat standardized messages back and forth. Then I can move parts of the code between the XCore and the Linux system and I only have to adjust the routing. The motivation is to be able to develop control code with e.g. Python on the ARM Linux host, and then move it to fast XMOS code once I've figured out what I want to do.
For this I'm planning to use transactions and a struct containing the header, followed by the actual information to send. In some cases, I may use channels between threads and direct communications, but likely just for the low-latency parts. I'm still figuring out exactly how I want the system to work, but I'm working with the goal of making it as similar on the application processor side and hardware side as possible.
User avatar
landon
Experienced Member
Posts: 71
Joined: Mon Sep 06, 2010 4:05 pm
Contact:

Post by landon »

After looking at this harder, unless I'm missing something big, I think it's coming down to:

1) use streaming channels to remove some of the synchronous issues (it helps but still have to somehow guard against a blocking channel input or output call if the channel's stream buffer is full).

2) Judicious choice of the order of select case statements.

For example, since I want to be able to type something at my controller (via the rx thread) and have it respond to me nearly instantly, I put the rx channel check as the first select case. Assuming it's not blocked down lower from a previous loop, any receive characters seem to get priority because it appears it's evaluated in order of case sequence.

I still wish there was such thing as a channel peek() - I see there is support for port peeks, but haven't seen anything like a peek for channel. Perhaps I just haven't stumbled far enough to find it.

Here's a version of the code with streaming channels. In this case, I artificially set up the rx/tx thread to loop back 100 characters. The results are that rx and tx chores will consume all the select/case opportunities through the loop...once the 100 characters are exhausted, the other threads can kick in and start producing and consuming via their channels. I put the rx channel as the first case and the last thing through the loop is another tx (loopback.) I think this proves that the execution is case order dependent because the other threads would have immediately stuff their channels so there should have been a case to take another case, but it doesn't until the rx/tx loop back is finished after 100 chars.

Code: Select all

/* channel-test.xc
 *   experiment with how channels work
 *   attempt to create a master controller which communicates via channels
 *   to all other threads
 */

#include <xs1.h>
#include <print.h>
#include <platform.h>
#include <xclib.h>
#include <string.h>
#include "xfifo.h"

#define FIFO_SIZE  (11 * 1024)

void acquire(streaming chanend controllerChan )
{
	while( 1 )
	{
		for( int i = 0; i < 10; i++ )
		{
			controllerChan <: 5;  // dummy data
			printstrln("Acquire putting 5 into acq channel");
		}
		while(1) ;  // spin loop
	}

}

void rx(streaming chanend controllerChan )
{
	while( 1 )
	{
		for( int i = 0; i < 100 ; i++ )
		{
			controllerChan <: (int)'a';  // dummy data
			printstrln("Rx Putting 'a' into receive channel");
		}
		while(1) ;  // spin loop
	}
}

void tx(streaming chanend controllerChan )
{
	unsigned int txData;

	while( 1 )
	{
		for( int i = 0; i < 100; i++ )
		{
			controllerChan <: txData ;  // get data from channel to send out
			printstrln("Tx Got data to transmit");
		}
		while(1) ;  // spin loop
	}

}

void monitor_fifo(streaming chanend controllerChan )
{
	while( 1 )
	{
		for( int i = 0; i < 10; i++ )
		{
			controllerChan <: 1;    // dummy
			printstrln("Fifo monitor Put 1 into fifo control channel");
		}

		while(1) ;  // spin loop
	}

}

void controller(streaming chanend rxChan, streaming chanend txChan, streaming chanend acqChan, streaming chanend fifoChan )
{
	unsigned int rxData, txData, acqData, fifoData;



	while( 1 )
	{
		txData = 0x00;

		select {

		case rxChan :> txData :
			// do something with rxData - make it txData like a loopback
			printstrln("Rx Data received");
			break;
		case acqChan :> acqData :
			// do something related to data acquired
			printstrln("Acq Data received");
			break;
		case fifoChan :> fifoData :
			// do something with fifoData received
			printstrln("Fifo Data received");
			break;
		}

		// default
		if ( txData != 0x00 )
		{
			printstrln( "Putting 100 into Tx channel");
			txChan :> txData;
			// change some status related to tx data
			printstrln( "Put 100 into Tx channel");
		}
	}
}

unsigned int fifobuf[FIFO_OVERHEAD + FIFO_SIZE];

int main()
{
	streaming chan rxChan, txChan, acqChan, fifoChan;

	xfifo_t fifo;
	fifo = xfifo_init( fifobuf, FIFO_SIZE);

	par {
		controller( rxChan, txChan, acqChan, fifoChan );
		monitor_fifo(fifoChan);
		acquire(acqChan);
		rx(rxChan);
		tx(txChan);
	}

	return 0;  // should never get here
}

Landon
Post Reply