Innovative Integration
 
Log inUsernamePassword
Log me on automatically each visit    
Register
Register
Log in to check your private messages
Log in to check your private messages
Toro - stream data to host
Goto page 1, 2, 3  Next
 
Post new topic   Reply to topic    II Support Forum Index -> Toro
View previous topic :: View next topic  
Author Message
Veolw



Joined: 25 Apr 2009
Posts: 48

PostPosted: Sat Apr 25, 2009 11:26 am    Post subject: Toro - stream data to host Reply with quote

Hello,

I have one interrupt handler (intEInt7_Handler) which use Servo class and realize some mathematical algorithms. Each call of intEInt7_Handler provide very important output which have to be send to host PC. What is best way to do it? Is it possible to send data within intEInt7_Handler or it is better to use some Thread? I have to be sure that all data are send to host so I don't know whether Thread is good solutions. I need to find reliable and fastest way.

I use Pismo library and PciOutStream class to be able to send data to host. Is that correct way?

Thank you very much
Back to top
View user's profile Send private message
jhenderson
Site Admin


Joined: 07 Mar 2006
Posts: 1177
Location: So. Cal. USA

PostPosted: Mon Apr 27, 2009 7:42 am    Post subject: Communications within ISR Reply with quote

Performing communications within an ISR is dangerous and must be coded with care.

Use of the PciOutStream class is forbidden. Interrupt routines must not call functions which block, or perform memory allocations (and a number of other restrictions).

I recommend that you implement a minimal queue which is used to store the result of your mathematical algorithms. When the ISR stores into this queue, it should release a semaphore. Create an application thread, which is blocking on this semaphore. It will awaken each time the ISR posts data into the queue and the thread will dispatch all of the data to the host.

This will be less deterministic than a direct transfer from within the ISR, but it will not violate the ISR restrictions.

_________________
Jim
Back to top
View user's profile Send private message Visit poster's website
Veolw



Joined: 25 Apr 2009
Posts: 48

PostPosted: Wed Jun 10, 2009 4:28 am    Post subject: Reply with quote

Ok, I finished my DSP side code but I have some problem Rolling Eyes

If I want to send some data from DSP to HOST is it enought to call following code on the DSP side?

Code:
PciOutStream *pciOutStream = new PciOutStream();
pciOutStream->Buffer().Bytes(23 * sizeof(float));
pciOutStream->Open();


If yes so I don't understant why PciOutStream.Open() doesn't return any result. It looks like blocking call.

Is it neccessary to create new instance of PciOutStream any time I want to send some data or is it possible to use one instance? I ask because ASnap example use following code:

Code:
while(!Terminated())

............

            // Open PCI output device, use same size buffers as Ain
            Pci = new PciOutStream();
            Pci->SizedAs(*Ain);
            if (!Pci->Open())
                SendBufferInfo(true,PciErr);

............

}


My next question is about data buffer size on the host side. I send (23 * sizeof(float)) Bytes to host every time when the intEInt7 interrupt occurs. Is it enought to set BlockStream.BufferSize(23 * sizeof(float)) on the host site or do I have to use some other buffer size?

Does PciOutStream class use DMA? I think yes but I ask to be 100% sure

Thank you.
Back to top
View user's profile Send private message
csmith
Site Admin


Joined: 13 Apr 2006
Posts: 55

PostPosted: Wed Jun 10, 2009 8:50 am    Post subject: Toro Streaming Questions Reply with quote

Thanks for the questions:

1) The code fragment you give in the first question is not sufficient to actually send data. Open() just prepares the internal structures and buffers to move data. No data is sent or recieved then. Its only return is a flag indicating if some error occurred (such as lack of memory to make its queues).

2) No, you should not recreate PciOutStream for each block sent. It is meant to have many blocks of data sent through it.

The following code fragment comes from the Stream example, which sets up two data streams between host and target. This is part of the thread that passes data from Analog Input to the PCI output...

Code:

            // Open the streams
            if (!AnalogI.Open())
                DynamicHang("Unable to open analog input stream");

            // Open PCI output device
            PciOutStream Pci;
            Pci.SizedAs(AnalogI);
            if (!Pci.Open())
                DynamicHang("Unable to open PCI output stream");

            while (Looping)
                {
                AnalogI.Get();

                // Perform application-specific processing here.
                // Use Analog.Buffer().Addr() to obtain address of
                // newly-filled data from A/Ds.  Use
                // Pci.Buffer().Addr() to obtain address of
                // pending output buffer to be sent to Host.
                // This example simply loops A/D data to PCI bus...

                Pci.Put(AnalogI.Buffer());
                }

            // Terminate streaming
            Pci.Close();
            AnalogI.Close();


So Open() is called before sending any data, but multiple Put() commands are done to send data to host. In this case, the data from the Analog In is pushed out directly by calling the Buffer() command. The SizedAs is used to make sure the sizes match.

In your ASnap example, the SendBufferInfo is NOT sending any data to the host - instead it is sending a message to the host containing information about the data to come - such as the size and number of buffers to expect. This is send over the interrupt given 'control' channel to coordinate the two sides. The actual data is sent later, in the loop later in the thread.

3) Host side restrictions

The host side has considerable restrictions on the size of its blocks. They must be a power of 2, and a reasonably large one at that. So if the host side size is 8K, you will not get notification until a number of your 23 word packets have been issued. I would suggest making the data packets you send fit evenly into your host blocks, to make it easier to pick out your blocks from the stream - pad out to 32 words.

The assumption of our streaming model is that more data will always be coming to 'push' any particular piece of data along until it arrives at the host. Expecting the transfer to act like a message and arrive at the host at once is very hard to arrange.

4) Yes the Pci streams and analog streams all use DMA. This reduces the demand on the processor markedly.

I hope these have helped.

_________________
Chris Smith
Innovative Integration
csmith@innovative-dsp.com
Back to top
View user's profile Send private message
Veolw



Joined: 25 Apr 2009
Posts: 48

PostPosted: Thu Jun 11, 2009 1:54 am    Post subject: Reply with quote

Thank you for your answer. I wrote bad example in my first question. Following example is correct:

Code:
PciOutStream *pciOutStream = new PciOutStream();
pciOutStream->Buffer().Bytes(23 * sizeof(float));
pciOutStream->Open();

float *outBufferPtr = pciOutStream->Buffer().FloatAddr();
float *tmpOutBufferPtr = NULL;

while (!Terminated())
{   
   tmpOutBufferPtr = outBufferPtr;

   // Send data to host.
      
   *tmpOutBufferPtr++ = data[0][1];
   *tmpOutBufferPtr++ = data[1][1];   
   *tmpOutBufferPtr++ = data[2][1];   
   *tmpOutBufferPtr++ = data[3][1];   
   *tmpOutBufferPtr++ = data[4][1];   
            ........

            pciOutStream->Put();
}


Is it correct way? If yes so why PciOutStream.Open() method doesn't return any result? Code after PciOutStream.Open() method never executes because of PciOutStream.Open() method.

Threfore I tried to remove

Code:
pciOutStream->Buffer().Bytes(23 * sizeof(float));


and code after PciOutStream.Open() executes. I'm not sure why my code doesn't work. To find some solution I tried to use following code:

Code:
PciOutStream *pciOutStream = new PciOutStream();
// pciOutStream->Buffer().Bytes(23 * sizeof(float));
AdcStream *adcStream = new AdcStream();
adcStream->Open();
pciOutStream->SizedAs(*adcStream);
pciOutStream->Open();


Now, code after pciOutStream->Open() executes but I don't understant why because SizedAs() do following:

Code:
Stream & Stream::SizedAs(Stream &Stream)
{
    FBuffer.Bytes(Stream.BufferSize());

    return *this;
}


and it is absolutely same as to use:

Code:
pciOutStream->Buffer().Bytes(23 * sizeof(float));


because pciOutStream->Buffer() do following:

Code:
IntBuffer & Stream::Buffer()
{
    return FBuffer;
}


Can you exaplain me what is wrong please? I don't need to use any Stream because I use PciOutStream to be able to send my internal buffer to host.

Thank you.
Back to top
View user's profile Send private message
csmith
Site Admin


Joined: 13 Apr 2006
Posts: 55

PostPosted: Fri Jun 12, 2009 8:17 am    Post subject: Streams Reply with quote

Part of your confusion is that Streams do a lot more buffer management than is apparent in the interface. A Stream actually holds a ring of buffers internally that all must be the same size. Calling SizedAs before Open makes the class resize ALL of the buffers to match. Resizing the one buffer you can't see after open doesn't fix the others, and problems will result if all are not the same size.

The first code sample has a problem. The sizing is done early enough, but the size might be too small. Leaving that aside, though, you can not save the pointer to Buffer and reuse it. Each time you call Put(), the filled buffer is rotated down into the Stream class to be output, and a empty buffer from the ring replaces it in the call to Buffer. So the address you save will be wrong most of the time.

You need to re-fetch the address after every Put() call.

You can use a user-created buffer. If you call Put() with a buffer, the contents of the two buffers are swapped, with the filled data being pushed into the stream and an empty buffer replacing it. This is what the AnalogIn example I sent before is doing, pushing data from its stream ring into the Pci stream's queue, and getting a empty PCI queue buffer in exchange,

Code:


IntBuffer FillBuffer(<same size as PciOutBuffer>)

while (!Terminated())
{
     //  Fill FillBuffer with data   

     pciOutStream->Put(FillBuffer);
}



Note that since FillBuffer's contents are replaced, you have to re-fill FillBuffer every time you put it. If you examine the address, you can see it rotate through several addresses. It is essential that the sizes of the buffers all match.

It wasn't clear to me before, but it appears that you are saying that Open does not complete when you open it with your 23 word size. My guess is that this is an illegal size.

You could try having your intermediate buffer hold 16 or so of your 23 word elements, or pad out the size to 32 words. The blocks you get on the host must be a power of 2 anyway, so you might as well make your transfer data line up properly to reduce the workload on the other side. And very small buffers aren't efficient, since you pay your DMA setup time costs very often.
You should really try and build up the size of the PCI buffers to something more reasonable.

_________________
Chris Smith
Innovative Integration
csmith@innovative-dsp.com
Back to top
View user's profile Send private message
Veolw



Joined: 25 Apr 2009
Posts: 48

PostPosted: Mon Jun 15, 2009 6:52 am    Post subject: Reply with quote

Thank you. Now I am able to send some data to host but I have problem.

On the DSP side I set PciOutStream.Buffer().Bytes(128). PciOutStream.Put() return 128 so I think Put() method work correctly.

On the host side I set BlockStream.BufferSize(128) but it doesn't work. If I set BlockStream.BufferSize(32) so everything works fine but OnDataAvailable occurs only one and I am not able to get other data.

I modified DSP side code as follows:

Code:
PciOutStream *pciOutStream = new PciOutStream();
pciOutStream->Buffer().Bytes(128);
pciOutStream->Open();

float *outBufferPtr = NULL;

while (!Terminated())
{   
   outBufferPtr = pciOutStream->Buffer().FloatAddr(); 

   // Send data to host.
       
   *outBufferPtr++ = data[0][1];
   *outBufferPtr++ = data[1][1];   
   *outBufferPtr++ = data[2][1];   
   *outBufferPtr++ = data[3][1];   
   *outBufferPtr++ = data[4][1];   
    ........

    pciOutStream->Put(); // Return 128.
}


I tried to remove while loop and send data only once but it didn't help to.

Thank you for some solution.
Back to top
View user's profile Send private message
Veolw



Joined: 25 Apr 2009
Posts: 48

PostPosted: Wed Jun 17, 2009 1:36 am    Post subject: Reply with quote

Hello,

I still didn't find some solution. I tried to change reserved memory by using the ReserveMemDsp applet but it didn't help.

If I set BlockStream.BufferSize(32) or BlockStream.BufferSize(16) or BlockStream.BufferSize(Cool everything works fine but if buffer size is bigger than 32 my program doesn't work.

I tried to send data to host by using IntBuffer and PciOutStream.Put(IntBuffer) method but it didn't help to.

I think my modified source code, which is posted in previous post, is correct.

I really need help Crying or Very sad

Thank you.
Back to top
View user's profile Send private message
csmith
Site Admin


Joined: 13 Apr 2006
Posts: 55

PostPosted: Wed Jun 17, 2009 2:14 pm    Post subject: Reply with quote

The minimum host busmaster block is 256 words - not bytes. The region is 8 sections of this size, in each direction.

The Block Stream model is not packet based. You can't send a tiny bit of data and expect it to arrive on the other side at once. You need to push additional data blocks out on the target to fill one of the regions, after which you will be notified and you will have several of your 128 byte sent packets available to process, all collected together in one block.

The closest we have to a message is the command messages, but these have limits of their own depending on what board you have.

If you are getting one interrupt, presumably you will get more if you send more data. If you send your 128 bytes every interrupt, after 8 interrupts you will have a packet filled in and you will get a notification. There's no reason why if you continue to send data, you will not eventually get the data across.

I suspect that you may be not keeping up with your analog interrupt from trying to send many small packets instead of one big one. To test this, you can just create data on the target regardless of your interrupt to get the data stream flowing. Send a 128 word packet and sleep 100 ms or so. You should be able to get this part working, and then try filling in those packets with real data after that is worked out.

_________________
Chris Smith
Innovative Integration
csmith@innovative-dsp.com
Back to top
View user's profile Send private message
Veolw



Joined: 25 Apr 2009
Posts: 48

PostPosted: Thu Jun 18, 2009 3:43 am    Post subject: Reply with quote

Thank you.

In fact, my host application need to work with packets. I need to send some buffer from the DSP side and I have to receive exactly same buffer on the host side at once.

What is best way to do this? Do I have to implement some logic which will create packets from received data, or is there some better solution to do this? I found some PacketStream class but I think it is not possible to use this class with Toro card.

My application has to be as fast as possible. I am not sure whether it is possible to do this by using messages.

Can I be sure that data sended by using PciOutStream are received by using BlockStream in the same order?

Thank you.

Thank you.
Back to top
View user's profile Send private message
Veolw



Joined: 25 Apr 2009
Posts: 48

PostPosted: Thu Jun 18, 2009 6:05 am    Post subject: Reply with quote

I would like to supplement my previous post.

It seems that BlockStream.BufferSize expect number of WORDS? Is it true? Sorry for this question but I didn't find this information in documentation.

I tried to set BlockStream.BufferSize(512) on the host side and on the DSP side I set PciOutStrem.Buffer().Bytes(512 * 4). Now it works fine.
Back to top
View user's profile Send private message
csmith
Site Admin


Joined: 13 Apr 2006
Posts: 55

PostPosted: Thu Jun 18, 2009 8:57 am    Post subject: Reply with quote

To answer the second question first - all sizes related to buffers on the host are 32 bit words, since that is the natural element size of our transfers. It is also the minimum chunk size of data from the analog - smaller devices are usually paired into 32 bit groups there too. Its easier to just use words and shift to bytes when needed.

When you apply a datagram to a buffer for inspecting its contents, then the datagram sizes are converted to the type of the applied type. For example, you have a buffer returned from the target. Buffer.SizeInInts() returns 1024. You apply a ShortDG datagram to that buffer and call Datagram.size(). This returns 2048, because there are 2048 short elements in a 1024 word buffer.

The sizing of the Busmaster Region is an exception, this size is in bytes usually. Lately we have tried to add unit size hints and conversions to make this clearer -- 'SizeInInts()' and so forth.

To the first question -

The stream system is designed for situations with continuous data flow. The more you try to fight that the more effort it will take.

For instance, lets take the sizes you were using before. IF the packet size is 512 words, the busmaster region is resized to contain room for 8 of these packets. When the target fills a region, then the host is notified and will read the full region and report to the application. If you send half a region, this data remains inaccessable in the middle. The target side also has queueing, so its possible that packets can stack up here, having been Put() but not busmastered.

If you send a 32 word packet each interrupt from the target, you will get an interrupt when the 512 word packet is filled on the host. This packet will contain 512/32 packets - 16 of em. All data is present and in the same order as sent.

Actually, you will not get notification until the target has sent MORE than the 16 packets across. The interrupt requires the 513th word to be sent to trigger the interrupt. (the section boundary must be crossed, not reached).

So in this case, the host is notified the instant you do the 17th Put() of 32 words. This removes the first 16 from the 'transfer' stage and leaves only message 17 in limbo. Subsequent interrupts will be moved forward, but the host will not respond until the next interrupt, at interrupt 33. Then you are again 1 behind in notifications.

So some of the skew can be reduced by sizing your transfer block to be the same as your busmaster block - but you still need to send a second block to push block N to the host application. This is just the way this communication system works.
I suppose if you sent 2 such packets per interrupt, with the good one first, the one packet in limbo would be a pad packet in every case.

In most cases, just sending extra data and accepting the time skew is acceptable. Even in a true packet system, there would be time skewing, since data transfer is not instant.

The PacketStream class does implement a packet system, but as you surmised will only work on boards that can use this method of communication. Toro is not one of those boards. The new scheme was developed to allow a more complicated data stream, with packets of arbitrary size and from multiple sources all mixed together. In that, you can send a small packet and expect the other side to get it at once. This system is a bit more complicated for us and for the user, since you have to look at the packets to figure out what data is even in them. Our newer cards use this method.

_________________
Chris Smith
Innovative Integration
csmith@innovative-dsp.com
Back to top
View user's profile Send private message
Veolw



Joined: 25 Apr 2009
Posts: 48

PostPosted: Tue Jun 23, 2009 8:21 am    Post subject: Reply with quote

Thank you Chris.

Now everything works fine. Therefore I have three general questions.

First Question:

I have II_EINT7_IntHandler and thread whose name is SendDataThread.

Implementation of interrupt handler is following:

Code:

void interrupt II_EINT7_IntHandler
{
   if (sendDataFlag)
      return;
   
   ...
   ... I realize some mathematical algorithm here and fill internal output buffer.
   ...
   
   sendDataFlag = true;
}


Implementation of SendDataThread Execute method is following:

Code:

void SendDataThread::Execute()
{
   while (!Terminated())
   {
      if (!sendDataFlag)
         continue;
      
      ...
      ... I fill PciOutStream buffer here and send data to host.
      ...
      
      sendDataFlag = false;
   }   
}


Because I have to be sure that all data, which are calculated within interrupt II_EINT7_IntHandler, will be send to host I use very simple synchronization with using sendDataFlag variable. Default value of this variable is false.

Now I would like to ask you. Is it correct solution to synchronize my thread and interrupt handler by using sendDataFlag variable or there is some better solution to do this? I use this method because I think that interrupt handler cannot be interrupt so there is no reason to use different way. Therefore, if my idea is bad so correct me please.

Second Question

If I want to read and change one variable by thread and interrupt handler I use volatile keyword to be sure that I work with same data. Can I apply volatile keyword to instance of some class? For example:

Code:


class MyDataClass
{
   public:
       int someVariable_1;
       int someVariable_2;
};

volatile MyDataClass myDataClass;



Is it correct solution or do I have to apply volatile keyword to all MyDataClass data members?


Third Question

I wrote above that I use II_EINT7_IntHandler. When HWI_INT7 exactly occurs? I ask because after each start of DSP application HWI_INT7 occurs twice. If I call IRQ_enable so this interrup occurs twice too.


Thank you.
Back to top
View user's profile Send private message
Veolw



Joined: 25 Apr 2009
Posts: 48

PostPosted: Tue Jun 23, 2009 8:41 am    Post subject: Reply with quote

I forgot to write that I use interrupt handler with Servo class.

Code:
Servo *servo = new Servo(intEInt7, false);
Back to top
View user's profile Send private message
csmith
Site Admin


Joined: 13 Apr 2006
Posts: 55

PostPosted: Tue Jun 23, 2009 9:54 am    Post subject: Signalling in interrupt handlers Reply with quote

The means you use to signal can work, but has a couple of consequences - one is that if you fall behind in sending data, you will lose one or more interrupt notifications. This happens if the flag is still false when the next servo cycle happens.

Another issue is that the thread is "busy-waiting" when there is no data to be sent. If SendDataFlag is false, the thread is using up the CPU testing Terminated and the flag over and over. This can block out any background or low priority processing you may have, as the spinning is still 'more important' since it is in a higher priority thread.

A way to fix this is to use a synchronization primitive as the 'flag'. The Semaphore class is an example. If the thread awaiting data waits on the semaphore to be signalled the thread is put to sleep and takes no cpu. The ISR can call the Release() method to increment the semaphore and release the thread.

A semaphore has a count, so if the thread falls behind the signals will be remembered. If you just save a single interrupts worth of data, this isn't worth much. The CaliQueue template is a simple data structure that is safe if one thread is filling and another is emptying it. It is a simple ring buffer. This could be used to save all the data for the thread to eventually send off. There is also the Queue class which is a wrapper around the TI CSL QUE interface. I do not know if this structure can be used in an interrupt handler, however. Many BIOS and CSL functions cannot be. The Semaphore release certainly can.

The Mailbox class is another wrapper around a DSP/BIOS service - the MBX. Again, I do not know if this particular method can be used in an interrupt handler. If the method blocks, it definitely cannot.

Declaring a class variable volatile

I don't know for sure how well this compliler implements the volatile keyword. Theoretically, what you did would make the entire class volatile. This does not mean that it is thread safe, or assure atomic access to the variable.

What volatile does do is that it will keep the optimizer from assuming that the contents of the variable have not changed by examining the code.
Code:


    int x = 5;

    int y = x;
    ...some code that doesn't use y or x

    y = x;


An optimizer is allowed to look at the above code and say that since x is 5 and doesn't change, then it can replace the entire thing with
Code:


    int x = 5;
    int y = 5;



thus never reading x at all. If x is changed by another thread, this is bad.

If you truly want to assure that two threads don't access a class' data incorrectly you use a mutex to assure that only one thread hits it at once. The TSQueue.h holds a class that makes the STL queue class thread safe by mutex guarding all accesses to it.

Of course, since the mutex blocks, you can't use this when one of the two sides is in an interrupt handler. A common way around that is to use the handler to only notifiy a worker thread that there is work to be done with a semaphore. This new thread reads the data and does the calculation:

Code:


Semaphore  InterruptHappened;
Semaphore  SendDataAvailable;

void interrupt II_EINT7_IntHandler
{
    InterruptHappened.Release();   // can do in an interrupt
}

void OnInterruptThread::Execute()
{
   while (!Terminated())
        {
        InterruptHappened.Acquire();     //  wait here for an interrupt
        ...
        ...  Read data from hardware
        ...
        ... I realize some mathematical algorithm here
        ...
       
        ...  push data into thread safe data structure for send thread
        ...     (can use ThreadSafeQueue, or BIOS Queue or Mailbox)

        //  Notify PCI sending thread
        SendDataAvailable.Release();

        }   
}

void SendDataThread::Execute()
{
   while (!Terminated())
   {
        SendDataAvailable.Acquire();    // wait for data
         continue;
     
      ...  Read data out of the queue or mailbox between threads
      ...
      ... I fill PciOutStream buffer here and send data to host.
      ...
   }   
}



Now no work is done in the interrupt handler, and you can use any BIOS utility safely in the two worker threads. This works very well in cases where the processing of the interrupt can be delayed. In the case of a servo, you need to calculate the output in the ISR, so the reading of the raw data and at least some processing would have to remain in the ISR. If there is some additonal calculations done just on the data to be sent down, it could be done in a worker thread as above.

Interrupt Count

I assume EINT7 is attached to the analog interrupt. I don't know right off hand why it might be firing an extra time at the start. You must make sure that any queue is set to be only one event deep when servoing. This may be the issue here.

_________________
Chris Smith
Innovative Integration
csmith@innovative-dsp.com
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    II Support Forum Index -> Toro (GMT - 8 Hours)
Goto page 1, 2, 3  Next
Page 1 of 3

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You cannot download files in this forum

© Copyright 2006-2008 Innovative Integration
Powered by phpBB © 2001, 2002 phpBB Group
Based on iCGstation v1.0 Template By Ray © 2003, 2004 iOptional