select statement in a thread

Technical questions regarding the XTC tools and programming with XMOS.
User avatar
boeserbaer
Active Member
Posts: 51
Joined: Fri Jan 29, 2010 4:36 pm

select statement in a thread

Post by boeserbaer »

How does the select statement work?
Are the equivalent of interrupts in involved?
Do i have to defend against recursion?

I am looking at the XC manual, and see the following regarding select case:

"Case statements are not permitted to contain output operations as the XMOS archi- tecture requires an output operation to complete but allows an input operation to wait until it sees a matching output before committing to its completion."
followed by the following code:
select {
....
case isTX => tmrTX when timerafter(txTime) :> void :
if (txI < 8)
TX <: >> txByte;
......
}
Which looks a lot like an output operation.
I am including my code for comment on correct usage. With the pollTimer commented out, it works properly. When un-commented, it stumbles and pauses.

Code: Select all

typedef enum {
		IDLE,
		CHECK_ADDR,
		GET_SEQ,
		GET_CMD,
		GET_CHANNEL,
		GET_DATA_SIZE,
		GET_CRC,
		GET_DATA,
		GET_DATA_CRC
} COMMS_STATE_t;
typedef enum {
		GET_SEQss,
		GET_CMDss,
		GET_CHANNELss,
		GET_DATA_SIZEss,
		GET_DATAss
} COMMS_STATESS_t;
typedef enum {
	SET_DAC = 0,
	GET_ADC = 1,
	SET_IO_BYTE = 2,
	GET_IO_BYTE = 3,
	SET_XRAY_ON = 4,
	SET_XRAY_OFF = 5,
	GET_XRAY_STATUS = 6,
	SET_RTC_BYTE = 7,
	GET_RTC_BYTE = 8,
	INIT_FLASH = 9,
	PAGE_FLASH = 10,
	FINISH_FLASH = 11,
	SET_POLLING =12,
	SET_POLL_INTERVAL =13,
	GET_GPIO = 0x00ab

} CMD_t;

void analogInterface(chanend rx, chanend tx, chanend status, chanend legacyRx, chanend legacyTx)
{
	COMMS_STATESS_t ss = GET_SEQss;
	unsigned cmd;
	int seq;
	int channel;
	unsigned dataSize;
	unsigned char cData[2*8];  // for now the only thing passed is a single 4 byte int, later, <8> 2 byte ints
	int charsRemaining;
	unsigned data;

	timer pollTimer;
	timer legacyTimer;
	unsigned int legacyTime;
	unsigned int pollTime;
	static int xraysON = 0;
	int val;
	unsigned tripVal;
	unsigned gpioVal = 0;
	unsigned seqss = 0;
	int	getKV = 0;
	unsigned kv = 128;
	unsigned ma = 64;
	unsigned legacyVal;
	unsigned dacValue = 0;
	unsigned char pollTable = 0;
	unsigned pollInterval = 500;


	p_spi1_select <: 11;
	spi1_init(1,100,20,0,0,0,0);   // ADC max clck = 2.5Mhz
//	expanderInit(xraysON);
	pollTimer :> pollTime;
	legacyTimer :> legacyTime;
	p_trip :> tripVal;
	status <: (tripVal & 0x0001);
	tx <: (int) 0;
	tx <: (unsigned) 0x00aa;
	tx <: (unsigned) 0;
	tx <: (unsigned) 4;
	uintToChar(cData,tripVal,4);
	for (int i=0;i<4;i++)
		tx <: (unsigned)cData[i];

	while (1)
	{
		select
		{
			case p_trip when pinsneq(tripVal) :> tripVal:
				status <: ((tripVal & 0x0001) | (gpioVal & 0x0002));
				tx <:  seqss++;
				tx <: (unsigned) 0x00aa;
				tx <: (unsigned) 0;
				tx <: (unsigned) 4;
				uintToChar(cData,tripVal,4);
				for (int i=0;i<4;i++)
					tx <: (unsigned)cData[i];
				break;
			case rx :> val:
				if (val == -1) {ss = GET_SEQss;}
				else {
					switch (ss){
					case GET_SEQss:
						seq = val;
						ss++;
						break;
					case GET_CMDss:
						cmd = val;
						ss++;
						break;
					case GET_CHANNELss:
						channel = val;
						ss++;
						break;
					case GET_DATA_SIZEss:
						dataSize = val;
						charsRemaining = dataSize;
						if (dataSize >0)
						{
							ss++;
						}
						else
						{
							ss = GET_SEQss;
							commandHandler (gpioVal, xraysON,pollTable, pollInterval, cmd,channel, cData,dataSize);
							status <: ((tripVal & 0x0001) | (gpioVal & 0x0002));
							tx <: seq;
							tx <: (unsigned) cmd;
							tx <: channel;
							tx <: dataSize;
							for (int i=0;i<dataSize;i++)
								tx <: (unsigned)cData[i];
						}
						break;
					case GET_DATAss:
						cData[dataSize - charsRemaining] = (unsigned char) val;
						charsRemaining--;
						if (charsRemaining <= 0)
						{
							ss = GET_SEQss;
							commandHandler (gpioVal, xraysON,pollTable, pollInterval, cmd,channel, cData,dataSize);
							status <: ((tripVal & 0x0001) | (gpioVal & 0x0002));
							tx <: seq;
							tx <: (unsigned) cmd;
							tx <: channel;
							tx <: dataSize;
							for (int i=0;i<dataSize;i++)
								tx <: (unsigned)cData[i];
						}
						break;
					default:
						break;
					}
				}
				break;

			case legacyTimer when timerafter(legacyTime) :> void:
				legacyTime += LEGACY_POLL_INTERVAL;
				if (dacValue < 4000)
					dacWrite(xraysON,(dacValue % 4),dacValue);
				dacValue++;
				if (dacValue > 6000)
					dacValue = 0;
				getKV = !getKV;
				if (getKV)
				{
					kv = adcRead(xraysON,KV_CHANNEL) >> 4;
					legacyTx <: kv;
				}
				else
				{
					ma = adcRead(xraysON,MA_CHANNEL) >> 4;
					ma |= 0x100;
					legacyTx <: ma;
				}
				break;
/*			case pollTimer when timerafter(pollTime) :> void:
				pollTime += pollInterval * TICKS_PER_us;
				tx <:  seqss++;
				tx <: (unsigned) 0x00aa;
				tx <: (unsigned) 0;
				tx <: (unsigned) 4;
				uintToChar(cData,tripVal,4);
				for (int i=0;i<4;i++)
					tx <: (unsigned)cData[i];

				break;
*/		}
	}
}

I am also including a second code snippet, which I think might be a more correct usage, where I post outputs to a queue, and execute using the default: statements Any comments greatly appreciated.

Code: Select all

void handleComms(chanend pg_ch, chanend rx_ch,chanend tx_ch,chanend analogRX_ch,chanend analogTX_ch, chanend flashRX_ch, chanend flashTX_ch )
{
	COMMS_STATESS_t ss = GET_SEQss;
	COMMS_STATESS_t ssA = GET_SEQss;
	char msg[] = "\n\rNEXT GEN XRAY CONTROLLER R16b\n\r";

	unsigned rxChar;
	int charsRemaining = 0;
	char rxString[8];
	COMMS_STATE_t cState;
	unsigned boardAddr = 0;
	int val;
	int useCRC=1;
	int LEDon = 0;
	timer t;
	unsigned int time;
	unsigned gpioVal;
	unsigned gpioValOld=0;
	unsigned ledSeq = 0;

	unsigned head = 0;
	unsigned tail = 0;
	unsigned addr;
	unsigned cmd[MAX_DEPTH];
	unsigned seq[MAX_DEPTH];
	unsigned channel[MAX_DEPTH];
	unsigned dataSize[MAX_DEPTH];
	unsigned crcRxVal;
	unsigned char cData[MAX_DEPTH][MAX_DATA_WORDS];
	unsigned crcDataRxVal;
	unsigned cmdssA;
	unsigned seqssA;
	unsigned channelssA;
	unsigned dataSizessA;
	int charsRemainingssA;
	unsigned char cDatassA[16];
	unsigned cmdss;
	unsigned seqss;
	unsigned channelss;
	unsigned dataSizess;
	int charsRemainingss;
	unsigned char cDatass[16];
	unsigned char buffer[BUFF_LEN];
	unsigned bufferHeadPtr = 0;
	unsigned bufferTailPtr = 0;
	unsigned pg5Vval;
	unsigned crcCalcVal;
	char crcString[MAX_DATA_WORDS*2];
	unsigned crcPtr = 0;
	char debugmsg[8];
	int charsRead;

	commsIO <: 6;
	for (int i=0; i<sizeof(msg); i++) {
      buffer[bufferHeadPtr++] = msg[i];
    }
	t :> time;
	time+=500000;
	crcInit();

	while (1)
	{
		select
		{
		case rx_ch :> rxChar:
			if (!( (cState == GET_CRC) || (cState == IDLE) || (cState == GET_DATA_CRC))) crcString[crcPtr++] = rxChar;
			if (rxChar == PREAMBLE_CHAR) {cState = IDLE;}
			if (rxChar == PREAMBLE_CHAR_NO_CRC) {cState = IDLE;}
			switch (cState)
			{
			case IDLE:
				if (rxChar == PREAMBLE_CHAR) {charsRemaining = 2; cState++; useCRC=1;  crcPtr = 0; if (DEBUG > 1) {tx_ch <: (unsigned)PREAMBLE_CHAR;}}
				if (rxChar == PREAMBLE_CHAR_NO_CRC) {charsRemaining = 2; cState++; useCRC=0;  crcPtr = 0; if (DEBUG > 1) {tx_ch <: (unsigned)PREAMBLE_CHAR_NO_CRC;}}
				break;
			case CHECK_ADDR:
				charsRemaining--;
				rxString[charsRemaining] = rxChar;
				if (charsRemaining <= 0) {
					charsRemaining = 2;
					addr = getHexUnsigned(rxString,2);
					if (addr == boardAddr) {if (DEBUG > 1) {tx_ch <: (unsigned)'a';} cState++;}
					else {if (DEBUG > 1) {tx_ch <: (unsigned)'x';} cState = IDLE;}
				}
				break;
			case GET_SEQ:
				charsRemaining--;
				rxString[charsRemaining] = rxChar;
				if (charsRemaining <= 0) {
					charsRemaining = 2;
					seq[head] = getHexUnsigned(rxString,2);
					if (DEBUG > 1) {tx_ch <: (unsigned)'s';}
					cState++;
				}
				break;
			case GET_CMD:
				charsRemaining--;
				rxString[charsRemaining] = rxChar;
				if (charsRemaining <= 0) {
					charsRemaining = 2;
					cmd[head] = getHexUnsigned(rxString,2);
					if (DEBUG > 1) {tx_ch <: (unsigned)'c';}
					cState++;
				}
				break;
			case GET_CHANNEL:
				charsRemaining--;
				rxString[charsRemaining] = rxChar;
				if (charsRemaining <= 0) {
					charsRemaining = 4;
					channel[head] = getHexUnsigned(rxString,2);
					if (DEBUG > 1) {tx_ch <: (unsigned)'h';}
					cState++;
				}
				break;
			case GET_DATA_SIZE:
				charsRemaining--;
				rxString[charsRemaining] = rxChar;
				if (charsRemaining <= 0) {
					if (DEBUG > 1) {tx_ch <: (unsigned)'s';}
					charsRemaining = 8;
					dataSize[head] = getHexUnsigned(rxString,4);
					cState++;
				}
				break;
			case GET_CRC:
				charsRemaining--;
				rxString[charsRemaining] = rxChar;
				if (charsRemaining <= 0) {
					if (DEBUG > 1) {tx_ch <: (unsigned)'r';}
					crcCalcVal = fullCRC(crcString,crcPtr) & 0xffffffff;
					crcRxVal = getHexUnsigned(rxString,8);
					if (crcRxVal == crcCalcVal){
						if (DEBUG > 1) {tx_ch <: (unsigned)'y';}
						if (dataSize[head]){
							charsRemaining =  2 * dataSize[head];
							crcPtr = 0;
							cState++;
						}
						else{
							head++;
							if (head >= MAX_DEPTH) { head = 0; }
							cState = IDLE;
						}
					}
					else {
						if (DEBUG > 1) {tx_ch <: (unsigned)'n';}
						cState = IDLE;
					}
				}
				break;
			case GET_DATA:
				charsRemaining--;
				rxString[charsRemaining % 2] = rxChar;
				if ((charsRemaining % 2) == 0){
					cData[head][(dataSize[head] - 1) - charsRemaining/2] = getHexUnsigned(rxString,2);
				}
				if (charsRemaining <= 0) {
					if (DEBUG > 1) {tx_ch <: (unsigned)'d';}
					charsRemaining = 8;
					cState++;
				}
				break;
			case GET_DATA_CRC:
				charsRemaining--;
				rxString[charsRemaining] = rxChar;
				if (charsRemaining <= 0) {
					if (DEBUG > 1) {tx_ch <: (unsigned)'r';}
					cState = IDLE;
					crcCalcVal = fullCRC(crcString,crcPtr) & 0xffffffff;
					if (DEBUG>1){
						tx_ch <: (unsigned)'[';
						while (crcPtr >0)
						{
							crcPtr--;
							tx_ch <: (unsigned)crcString[crcPtr];
						}
						tx_ch <: (unsigned)']';

					}
					crcRxVal = getHexUnsigned(rxString,8);
					if (crcRxVal == crcCalcVal){
						if (DEBUG > 1) {tx_ch <: (unsigned)'y';}
						head++;
						if (head >= MAX_DEPTH) { head = 0; }
					}
					else
					{
						if (DEBUG > 1) {tx_ch <: (unsigned)'n';}

					}
				}
				break;
			}
			break;
		case 	flashTX_ch :> val:
			switch (ssA){
			case GET_SEQss:
				seqssA = val;
				ssA++;
				break;
			case GET_CMDss:
				cmdssA = val;
				ssA++;
				break;
			case GET_CHANNELss:
				channelssA = val;
				ssA++;
				break;
			case GET_DATA_SIZEss:
				dataSizessA = val;
				charsRemainingssA = val;
				if (dataSizessA >0)
				{
					ssA++;
				}
				else
				{
					ssA = GET_SEQss;
					bufferHeadPtr = responseString(buffer, bufferHeadPtr, bufferTailPtr, boardAddr, seqssA, cmdssA, channelssA, dataSizessA, cDatassA);
				}
				break;
			case GET_DATAss:
				cDatassA[dataSizessA - charsRemainingssA] = (unsigned char)val;
				charsRemainingssA--;
				if (charsRemainingssA <= 0)
				{
					bufferHeadPtr = responseString(buffer, bufferHeadPtr, bufferTailPtr, boardAddr, seqssA, cmdssA, channelssA, dataSizessA, cDatassA);
					ssA = GET_SEQss;
				}
				break;
			}
			break;

		case analogTX_ch :> val:
			if (val == -1) {ss = GET_SEQss;}
			else {
				switch (ss){
				case GET_SEQss:
					seqss = val;
					ss++;
					break;
				case GET_CMDss:
					cmdss = val;
					ss++;
					break;
				case GET_CHANNELss:
					channelss = val;
					ss++;
					break;
				case GET_DATA_SIZEss:
					dataSizess = val;
					charsRemainingss = val;
					if (dataSizess >0)
					{
						ss++;
					}
					else
					{
						bufferHeadPtr = responseString(buffer, bufferHeadPtr, bufferTailPtr, boardAddr, seqss, cmdss, channelss, dataSizess, cDatass);
						ss = GET_SEQss;
					}
					break;
				case GET_DATAss:
					cDatass[dataSizess - charsRemainingss] = (unsigned char)val;
					charsRemainingss--;
					if (charsRemainingss <= 0)
					{
						bufferHeadPtr = responseString(buffer, bufferHeadPtr, bufferTailPtr, boardAddr, seqss, cmdss, channelss, dataSizess, cDatass);
						ss = GET_SEQss;
					}
					break;
				}
			}
			break;
		case pg_ch :> pg5Vval:

 			buffer[0] = 'V'; // we want immediate send
			bufferHeadPtr = 1;
			bufferTailPtr = 0;
			break;
		case t when timerafter(time) :> void:
			ledSeq++;
			if ((ledSeq % 100) == 0)
			{
				ledSeq = 0;
				LEDon = !LEDon;
				if (LEDon) {commsIO <: 14;}
				else {commsIO <: 6;}
			}
			time+=500000;
			gpio :> gpioVal;
			gpioVal &= GP_IN_PORT_MASK;
			if (gpioVal != gpioValOld) {
				gpioValOld = gpioVal;
				cmd[head] = GET_GPIO;
				seq[head] = 0;
				channel[head] = 0;
				dataSize[head] = 4;
				uintToChar(cData[head],gpioVal,4);
				head++;
				if (head >= MAX_DEPTH) { head = 0; }
			}
			break;
		default:
			if (head != tail)
			{
				switch (cmd[tail]) {
 				case SET_RTC_BYTE:
 					flashRX_ch <: seq[tail];
 					flashRX_ch <: cmd[tail];
 					flashRX_ch <: channel[tail];
 					flashRX_ch <: dataSize[tail];
 					for (int i =0; i<dataSize[tail];i++){
 						flashRX_ch <: (unsigned) cData[tail][i];
 					}
					break;
				case GET_RTC_BYTE:
 					flashRX_ch <: seq[tail];
 					flashRX_ch <: cmd[tail];
 					flashRX_ch <: channel[tail];
					flashRX_ch <: dataSize[tail];
 					for (int i =0; i<dataSize[tail];i++){
 						flashRX_ch <: (unsigned) cData[tail][i];
 					}
					break;
				case INIT_FLASH:
 					flashRX_ch <: seq[tail];
 					flashRX_ch <: cmd[tail];
 					flashRX_ch <: channel[tail];
					flashRX_ch <: dataSize[tail];
 					for (int i =0; i<dataSize[tail];i++){
 						flashRX_ch <: (unsigned) cData[tail][i];
 					}
					break;
				case PAGE_FLASH:
 					flashRX_ch <: seq[tail];
 					flashRX_ch <: cmd[tail];
 					flashRX_ch <: channel[tail];
					flashRX_ch <: dataSize[tail];
 					for (int i =0; i<dataSize[tail];i++){
 						flashRX_ch <: (unsigned) cData[tail][i];
 					}
					break;
				case FINISH_FLASH:
 					flashRX_ch <: seq[tail];
 					flashRX_ch <: cmd[tail];
 					flashRX_ch <: channel[tail];
					flashRX_ch <: dataSize[tail];
 					for (int i =0; i<dataSize[tail];i++){
 						flashRX_ch <: (unsigned) cData[tail][i];
 					}
					break;
				default:
					analogRX_ch <: seq[tail];
					analogRX_ch <: cmd[tail];
					analogRX_ch <: channel[tail];
					analogRX_ch <: dataSize[tail];
 					for (int i =0; i<dataSize[tail];i++){
 						analogRX_ch <: (unsigned) cData[tail][i];
 					}
					break;
				}
				tail++;
				if (tail >= MAX_DEPTH) {tail = 0;}
			}
			if (bufferHeadPtr != bufferTailPtr)
			{
				tx_ch <: (unsigned) buffer[(bufferTailPtr++)%BUFF_LEN];
				if (bufferTailPtr >= BUFF_LEN) {bufferTailPtr = 0;}
			}
			break;
		}
	}
}


User avatar
Berni
Respected Member
Posts: 363
Joined: Thu Dec 10, 2009 10:17 pm

Post by Berni »

A select simply waits until one of the statements inside a case are true (Timer or channel communication) If you use a default inside it it will go to that rather than wait if none of the cases are true.The popular use of it is to try to get data from a channel but without blocking if the data is not there yet.
User avatar
boeserbaer
Active Member
Posts: 51
Joined: Fri Jan 29, 2010 4:36 pm

Post by boeserbaer »

That was my understanding, It is true then that recursion is not an issue?
User avatar
Berni
Respected Member
Posts: 363
Joined: Thu Dec 10, 2009 10:17 pm

Post by Berni »

What do you mean specifically with recusion?
User avatar
Woody
XCore Addict
Posts: 165
Joined: Wed Feb 10, 2010 2:32 pm

Post by Woody »

boeserbaer wrote:How does the select statement work?
Are the equivalent of interrupts in involved?
Do i have to defend against recursion?
Yes the equivalent of interrupts are used. They are called events: like interrupts they occur as soon as the eventing condition occurs, but unlike interrupts they do not return so they are more like a branch than a call to a subroutine.

The select statement sets up the various resources to create an event if they occur, and exactly one of the events is taken within the select statement. There is no possibility of two being executed or one of them starting to execute and then being 'interrupted' by another executing.
boeserbaer wrote:I am looking at the XC manual, and see the following regarding select case:

"Case statements are not permitted to contain output operations as the XMOS archi- tecture requires an output operation to complete but allows an input operation to wait until it sees a matching output before committing to its completion."
followed by the following code:
select {
....
case isTX => tmrTX when timerafter(txTime) :> void :
if (txI < 8)
TX <: >> txByte;
......
}
If you look in the code 'isTX' is actual defined as a timer. The way I remember what's valid is that inputs use the '=>' operator and are valid, outputs use the '<=' operator and are not valid.
omega7
Active Member
Posts: 32
Joined: Thu Jun 03, 2010 12:16 pm

Post by omega7 »

hi,

A somewhat old case here, but my question concerns the 'default' select.

My feeling is that when the default select exists, but nothing in it (or lets say just setting a single variable) that the select does not handle other events properly. I think the processor is too busy with setting the variable ;). Is this correct?

Should I add a sleep() or something in the default case giving events and other threads execution time?

Thanks!
Martin
User avatar
Bianco
XCore Expert
Posts: 754
Joined: Thu Dec 10, 2009 6:56 pm

Post by Bianco »

omega7 wrote:hi,

A somewhat old case here, but my question concerns the 'default' select.

My feeling is that when the default select exists, but nothing in it (or lets say just setting a single variable) that the select does not handle other events properly. I think the processor is too busy with setting the variable ;). Is this correct?

Should I add a sleep() or something in the default case giving events and other threads execution time?

Thanks!
Martin
You don't have to add a sleep to your default case. The default case is only executed when there are no event available. Besides that in most situations you wouldn't use a default case if the select runs in a infinite loop.
omega7
Active Member
Posts: 32
Joined: Thu Jun 03, 2010 12:16 pm

Post by omega7 »

Thank you for your immediate response. What about the processor load question, is my feeling correct?
Is there something like 'processor load' within XMOS?
User avatar
Bianco
XCore Expert
Posts: 754
Joined: Thu Dec 10, 2009 6:56 pm

Post by Bianco »

omega7 wrote:Thank you for your immediate response. What about the processor load question, is my feeling correct?
Is there something like 'processor load' within XMOS?
As you problably know the processor can run multiple hardware threads simultaneously. So events on other threads can still be handled. Each thread gets a minimum amount of MIPS so there won't be starvation. However with running more than 4 threads the processor usage of one thread might influence the maximum amount of MIPS that other threads get.

When running a select case in a infinite loop with a default case the thread will never block and use all the MIPS it gets and hence might influence the performance of other threads.

To better understand the implementation of a select with default case, the following steps are taken:
1. Preparing the resource(s): enabling the generation of events/interrupts; set up the event vectors.
2. Globally (thread-level) enable events
3. If there is an event -> 3a else 3b.
3a. execute the event handler
3b. disable the global events, execute the default case

When an event triggers the global events will be disabled automatically.
If the select statement is in a loop it will jump back to either step 1 or 2 (depending on optimization and maybe other parameters).

Take for example the following code:

Code: Select all

#include <platform.h>
#include <xs1.h>

void test(chanend c, timer t)
{
	unsigned time;
	unsigned data;

	t :> time;
	time += 100;

	while (1) {
		
		select {
			case c :> data:
				asm("/* channel handler */");
				break;
			case t when timerafter (time+100) :> time:
				asm("/* timer handler */");
				break;
			default:
				asm("/* default handler */");
				break;
		}
	}
}
Compiled with -O2 -S on desktop tools 10.4.2 we get:

Code: Select all

test:
.L2:
          setc      res[r1], 0x1
.L54:
          in        r2, res[r1] 
.L10:
          ldc       r3, 0x64
          add       r2, r2, r3
.L51:
          clre      
          ldap      r11, .L25
          setv      res[r0], r11
          eeu       res[r0]
          setc      res[r1], 0x9
          ldap      r11, .L29
          setv      res[r1], r11
          eeu       res[r1]
.L42:
.L41:
          add       r4, r2, r3
          setd      res[r1], r4
.xtabranch .L29,.L25
          setsr     0x1
          clrsr     0x1
          /* default handler */
          bu        .L41 
.L25:
          chkct     res[r0], 0x1 
          outct     res[r0], 0x1 
.L55:
          in        r4, res[r0] 
          chkct     res[r0], 0x1 
          outct     res[r0], 0x1 
          /* channel handler */
          bu        .L41 
.L29:
.L56:
          in        r2, res[r1] 
          /* timer handler */
          bu        .L41 
- at label .L51 the resources are prepared (step 1)
- at label .L41 the value on which the timer should generate an event is set and after that events are globally enabled ( setsr 0x1) and after that disabled ( clrsr 0x1). When there is an event the clrsr instruction will not be executed and the processor will jump to the event vector of the resource that generated the event. Due to the loop it jumps back to .L41 after handling an event and the timer value
omega7
Active Member
Posts: 32
Joined: Thu Jun 03, 2010 12:16 pm

Post by omega7 »

Thanks Bianco! This is helpful. You should write an article about this. Some extra background info helps the programmer understanding what he is actually doing.... XC is wonderful, but sometimes you got stuck in unexpected behaviour. Good work.