Cross thread access of registers

Technical questions regarding the XTC tools and programming with XMOS.
GerhardNorkus
Active Member
Posts: 55
Joined: Wed Jan 05, 2011 2:15 pm

Cross thread access of registers

Post by GerhardNorkus »

Hello all. I'm quite new to the forum stuff. Have managed to insulate myself from it over the years.

I'm using inline assembly in XC to create some high speed code to access multiple cameras. I need a way to implement a reset pin such that various threads will immediately halt. I've tried using the following command:

set t[r11]:r10, r0

It's supposed to allow me to use the thread id to store r0 from my current thread in r10 of the thread specified by thread id r11. But that thread only manages to halt. Any ideas as to what to do? I have a suspicion that the error is ET_RESOURCE_DEP, but I'm not quite sure.


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

Post by segher »

You cannot use TSETR to store to a register that is currently being used.

One way to kill threads without those threads cooperating is to use debug
mode, e.g. via DCALL.

A nicer way to kill threads is to give all threads that need to be killed a
channel end with an interrupt on it, and then send to that channel end from
your master thread.

I recommend using real assembler (i.e. a *.s file) instead of inline asm for
this kind of thing.
GerhardNorkus
Active Member
Posts: 55
Joined: Wed Jan 05, 2011 2:15 pm

Post by GerhardNorkus »

segher wrote:You cannot use TSETR to store to a register that is currently being used.

One way to kill threads without those threads cooperating is to use debug
mode, e.g. via DCALL.

A nicer way to kill threads is to give all threads that need to be killed a
channel end with an interrupt on it, and then send to that channel end from
your master thread.

I recommend using real assembler (i.e. a *.s file) instead of inline asm for
this kind of thing.
Thanks! One more question. Say suppose I have the following (pseudo code). Assume that the outCameraData has previously been set up correctly to be a fifo:

unsigned long GlobalVariable ;
out buffered port:8 outCameraData ;
in port inCypressCtl0 = XS1_PORT_1K ;
out port outCypressRdy0 = XS1_PORT_1L ;
in port inFIFOReset = XS1_PORT_1H ;

void MyThread()
{
startofthread:
DoSomeInitialization();
while(1)
{
asm ("ldw r2, dp[GlobalVariable]"); // Address of Global Variable
asm ("ldw r1, dp[outCameraData]") ; // Address of Port 8A (outCameraData)

asm("MyThread_InitAsm:");
asm("setc res[r1], 0x17") ; // Clear the outcameraData buffer

// tight loop area
asm("MyThread_TightLoop:") ;
asm ("ldw r0, r2[0]");
asm("out res[r1], r0");
asm("bu MyThread_TightLoop");
}
}

void MyResetHandler()
{
asm ("ldw r7, dp[inFIFOReset]");
asm ("MyResetHandler1:");
asm ("in r0, res[r7]");
asm ("bt r0, MyResetHandler1"); // Only break out when reset goes low

// Do something here to break MyThread out of it's tight loop and go to it's MyThread_InitAsm label
// HELP ME!!!

asm ("bu MyresetHandler1") ; // Start looking for a reset signal again
}

int main()
{
configure_clock_src(clkFifoOut, inCypressCtl0) ;
configure_out_port_strobed_master(outCamData, outCypressRdy0, clkFifoOut, 0);
start_clock ( clkFifoOut ) ;

par
{
MyThread();
MyResetHandler();
}
}


Yeah, I know I need to learn the assembler, so consider this part of my education.

The above code will pause at the tight loop area's call of the 'out' command and block until the host device reads the bytes off of Port 8A. This port has been configured using a 1 bit pin as a clock, and is a strobed master. How would you cause the thread to jump to the MyThread_InitAsm label without inserting extra code in the tight loop area? How could I use the DCall as you recommend? Is there some better way? Is it better to use an event/interrupt of some sort? Is the MyResetHandler code unnecessary?
mculibrk
Active Member
Posts: 38
Joined: Tue Jul 13, 2010 2:57 pm

Post by mculibrk »

Well....
if you already use in-line asm and supposing all your threads are running on a single core, the simplest way could be to jut use a "global variable" to signal the "termination"...
If you use just ONE writer (the thread/procedure signaling the termination) and a simple "var" (ie int) and many readers it should be ok.

something like this:

Code: Select all

unsigned long GlobalVariable[2] ;    // use array to speedup lookup later
out buffered port:8 outCameraData ; 
in port inCypressCtl0 = XS1_PORT_1K ;
out port outCypressRdy0 = XS1_PORT_1L ;
in port inFIFOReset = XS1_PORT_1H ;

void MyThread()
{
startofthread:
DoSomeInitialization();
while(1)
{
asm ("ldw r2, dp[GlobalVariable]"); // Address of Global Variable
asm ("ldw r1, dp[outCameraData]") ; // Address of Port 8A (outCameraData)

asm("MyThread_InitAsm:");
asm("setc res[r1], 0x17") ; // Clear the outcameraData buffer

// tight loop area
asm("MyThread_TightLoop:") ;
asm ("ldw r0, r2[0]");
asm("out res[r1], r0");
asm("ldw r3, r2[1]");
asm("brbt r3, MyThread_TightLoop");
}
}

void MyResetHandler()
{
asm ("ldw r7, dp[inFIFOReset]");
asm ("MyResetHandler1:");
asm ("in r0, res[r7]");
asm ("bt r0, MyResetHandler1"); // Only break out when reset goes low

// Do something here to break MyThread out of it's tight loop and go to it's MyThread_InitAsm label
// HELP ME!!!

GlobalVariable[1]=0;

asm ("bu MyresetHandler1") ; // Start looking for a reset signal again
}

int main()
{
configure_clock_src(clkFifoOut, inCypressCtl0) ;
configure_out_port_strobed_master(outCamData, outCypressRdy0, clkFifoOut, 0);
start_clock ( clkFifoOut ) ;
GlobalVariable[1] = 1;
par
{
MyThread();
MyResetHandler();
}
}
and, do not forget to initialize the GlobalVariable[1]=1 on start

Also, you can use just a plain global var but I just wanted to "optimize" the access giving that you already use memory access based on GlobalVariable....

hope this helps,
regards
mculibrk
GerhardNorkus
Active Member
Posts: 55
Joined: Wed Jan 05, 2011 2:15 pm

Post by GerhardNorkus »

OK. Discovered how to do it without adding extra checks. This is for anyone using inline or regular assembler.

In the code below, I use thread events to do what I want. I havent figured out how to get all threads to respond to the same event (i.e. XS1_PORT_1H). For my application, I'm making a high bandwidth FIFO (around 33MHz for byte to word sizes). I want a master reset to work on both input and output threads, so I'll actually use 2 input pins instead of a single pin. This code has been demonstrated to work. Just remember, it doesn't store your registers or the pc when the event is called! It simply goes to the event vector (in this case Input Reset Vector).

in port inFIFOReset = XS1_PORT_1H ;
in buffered port:8 inCameraData = XS1_PORT_8A ;
unsigned char FIFOBuffer[16384] ;

void MyThread()
{
asm ("InitializationLabel:") // Label the beginning of your thread loop

asm (" clre"); // Clear the event flags for this thread
asm (" ldw r0, dp[inFIFOReset]"); // Get the resource address for inFIFOReset
asm (" ldc r4, 0"); // We'll be looking for a low signal (0)
asm (" setd res[r0], r4"); // on inFIFOReset
asm (" setc res[r0], 0x11"); // Make sure it looks for Condition Equal to zero
asm (" ldap r11, InputResetVector"); // Get the vector location for event handling
asm (" setv res[r0], r11"); // Put the vector in the resource table
asm (" eeu res[r0]"); // Set the Events Enabled status of the resource
asm (" setsr 0x1"); // Set the thread's state register to look for events

// Don't use the wait statements, just continue with your code here
asm (" ldaw r2, dp[FIFOBuffer]") ;
asm (" ldw r1, dp[inCameraData]") ;
asm (" ldc r6, 0") ;

asm ("InnerTightLoop:") ;
asm(" in r0, res[r1]") ; // Get camera data
asm(" st8 r0, r2[r6]") ; // store it in FIFO buffer
asm(" add r6, r6, 1") ; // increment storage address
asm(" bu InnerTightLoop") ; // branch unconditionally

// This is where the code jumps to when it receives a low signal on
// the inFIFOReset pin.
asm ("InputResetVector:") ;

// Do some useful tidy up...
asm (" ldc r0, 0") ; // Dummy code
asm (" bu InitializationLabel") ; // branch back to beginning
mculibrk
Active Member
Posts: 38
Joined: Tue Jul 13, 2010 2:57 pm

Post by mculibrk »

OK.... but in this example your're doing all in just one thread/procedure.

I understood, you need a mechanism to "signal" the reset "event" to different threads/procedures at least from another part of your code.
Anyway, if this works for you then all solved.

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

Post by segher »

GerhardNorkus wrote:I havent figured out how to get all threads to respond to the same event (i.e. XS1_PORT_1H).
Have one thread react to the I/O port; give the others a channel end to react to. The thread that
reacts to the I/O writes to the channel ends of the others.
Just remember, it doesn't store your registers or the pc when the event is called! It simply goes to the event vector (in this case Input Reset Vector).
Use an interrupt instead of an event, if you care.
TjBordelon
Active Member
Posts: 39
Joined: Mon Jul 29, 2013 4:41 pm

Post by TjBordelon »

I am having to solve a similar thing... Something happens and a handful of threads must react.

I guess you can't directly have everyone check the port. I haven't tried this but I bet that would fail. So I think having to notify the other threads on your own is unavoidable.

Instead of channels, couldn't you write your own "signal" and just use lock in assembly to implement? I'm about to try this. Will let you know if it works.

The benefit with this approach is that if you have a bunch of threads all waiting, they can all be sent off with a single instruction instead of having to send to each.

I have a bunch of threads waiting on an event and want them to all go at the exact same time. Typically "signal" is the way to go in traditional mindthink :)
TjBordelon
Active Member
Posts: 39
Joined: Mon Jul 29, 2013 4:41 pm

Post by TjBordelon »

Update: Can't do it! I'd still suggest locks for this since you don't tie up a channel. But sadly there is no way to wait on a lock without actually locking it. So only one thread would "wake up". You could quickly do a "lock/unlock" which would eventually let all waiting threads see the signal, but they would be staggered and won't go at the same time.
User avatar
segher
XCore Expert
Posts: 844
Joined: Sun Jul 11, 2010 1:31 am

Post by segher »

TjBordelon wrote:I have a bunch of threads waiting on an event and want them to all go at the
exact same time.
Only one thread is running at a time (per CPU); perhaps you have some
less exact definition of "exact" in mind? :-)