4 Replies Latest reply on May 6, 2019 5:00 AM by KandlaguntaR_36

    Can't figure out DMA...

    EtWh_2921621

      I have inherited some firmware code for our CX3 chip, which is connected to an OV4689 sensor and streaming video over USB 3 to the PC. The person who wrote the firmware code originally needed to send/receive (3K worth of) camera calibration data over USB, and couldn't figure out how to do that. So, they created separate dma channels to send and to receive "side-band data" to/from the PC. I don't understand this code very well. I would have thought ONE dmachannel, bi-directional, would be enough, but this person created two...

       

      The way it's supposed to work is by handshake. The PC sends a request to the firmware, the firmware processes the request, and sends a reply. The firmware never issues a DMA request to the PC first. This should make it straight-forward.

       

      When the PC sends a request to the CX3, I get notified in the DMA callback. For whatever reason, the programmer wrote it so there is a background, application thread, and when the callback gets called that a DMA buffer was received, an event is set, and the background thread goes and reads the event and does what is necessary (like send calibration data).

       

      The DMA "produced" callback calls DMAChannelGetBuffer( RecvStream ), then reads the packet, then calls DmaChannelDiscardBuffer( ), then sets the event for the background thread. The background thread then calls DmaChannelGetBuffer ( SendStream ), copies the calibration data, then calls DmaChannelCommitBuffer( ).

       

      On the PC side, I've opened up an overlapped IO file handle and am sitting there waiting for reads from the sideband USB channel. No rocket science there.

       

      NOW WHAT IS WEIRD:

       

      If anywhere in the background thread, I call CyU3PThreadSleep(), even with a timeout of just 1, the DMA transfer won't happen. DmaChannelGetBuffer( SendStream ) will sit there and time out! A timeout would seem to imply the send buffer is "busy", but since this is the first packet I'm sending over DMA, I don't see how it could be busy. There is something else weird at work here.

       

      Anybody have any good advice, thoughts?

        • 1. Re: Can't figure out DMA...
          KandlaguntaR_36

          By default, the USB device must have a control endpoint to do IN and OUT transactions. This is called Control Transfers. This is bi-directional.

           

          The other transfers: Bulk, Isoc and Interrupt endpoints need to have individual endpoints for both IN and OUT data transfer. This is uni-directional. Therefore, we need a separate DMA channel for each direction.

           

          Can you confirm the other thread in when you put the background thread to sleep? You may toggle a GPIO in other thread? or you may put debug print in it and print over UART.

           

          Please check whether CyU3PThreadRelinquish API is called; this allows other ready threads to run.

           

          You may refer AN75779 example firmware in which we have handled two threads.

          • 2. Re: Can't figure out DMA...
            EtWh_2921621

            let me write some more info...

             

            First, some definitions. Let's call the background thread the "sleep thread". Also, if I delay on the Sleep thread by calling Sleep() or I2C_SensorRead/Write, let's just call that "add delay".

             

            On the first time through the test the PC sends a command over USB to the CX3. This works fine. The PC is now going to read the reply from the CX3. It has a handle to the USB file IO and calls WinUsb_ReadFile() (along with overlapped IO). The CX3 chip issues a Dma GetBuffer() followed by a Commit(). Both of these calls return fine, but the DMA callback routine for the send never gets called. So it's committed the buffer, but the PC just can't read it.

             

            On the PC side, since I'm using overlapped IO, it waits for SingleObjectEx( ) (alertable) and returns with nothing transferred. I'm guessing something's gotten plugged up on the PC side.

             

            I had the bright idea this morning of calling WinUsb_FlushPipe( ) and trying again, an guess what? It works. For whatever reason, that unplugs the pipe and if I attempt the read call again, it works.

             

            So, now it looks like the problem is actually on the PC's side. Obviously, the pipes WORK because I can send/receive between the two pipes and get information flowing. The Send TO the CX3 never fails, and it's always the receive that doesn't work right. I don't know if it's because of how the pipes were set up, or if it's because I'm not doing something right on the PC end, or if it's because when a Send is performed (from the PC's side), it fouls up something on the receive side, and only a WinUsb_FlushPipe helps recover it.

             

            Plus, on top of it all, it seems to be related to timing. If I send the reply from the CX3 IMMEDIATELY (no waiting, sleeping, calls to I2C_XXX), then the Send happens fine, and I don't need to call FlushPipe. So why, after a PC->CX3 send, is there this little tiny window where the CX3 can reply okay, but wait a millisecond and things go wrong?

             

            Perhaps easier still, I'd like somebody on this forum to explain DMA channels. I want bidirectional communication between the PC and the CX3. I don't care how this is done. Currently, it's being set up with two DMA channels, each one with their own pair of sockets. This seems silly, is it necessary? Any socket I set up on a PC to PC transfer allows me to communicate both directions. And what is up with the socket #'s on the CX3?

             

            for the CX3 receive channel, it's a single channel, set up like this:

            dmaChannelConfig.prodSckId = CY_U3P_UIB_SOCKET_PROD_6;

            dmaChannelConfig.consSckId = CY_U3P_CPU_SOCKET_CONS;

            dmaChannelConfig.dmaMode = CY_U3P_DMA_MODE_BYTE;

             

            for the CX3 send channel, it's also a single channel, and set up like this:

            dmaChannelConfig.prodSckId = CY_U3P_CPU_SOCKET_PROD;

            dmaChannelConfig.consSckId = CY_U3P_UIB_SOCKET_CONS_6; // this is the socket we send it to

            dmaChannelConfig.dmaMode = CY_U3P_DMA_MODE_BYTE;

             

            I don't know how to interpret this. For the CX3, the receive dma is set up so that the consumer is the CPU, so that means the CX3 is the one that ocnsumes the packet. So the producer of the packet is PROD_6? What the heck is PROD_6? I guess the PC has connected over PROD_6 and will send it data?

             

            On the CX3 send side, the producer is the CX3, and the consumer is CONS_6...

             

            On both channels, DmaChannelSetXFer( ) has been set with a size of 0, which means "infinite transfer". But infinite transfer is never described in the CX3 docs. What's that mean? Is setting 0 a bad thing? Normally, packets of size 1024 are sent back and forth between the two, and this is expected. I have no need to "transfer forever".

             

            any example code out there for showing a CX3 set up to communicate over a side-band channel through DMA to the PC? Really, all we're trying to do is get camera calibration TO and FROM the CX3. Honestly, we can't figure out how to get that 3K worth of data to transfer over IAMCameraControl or some other means of transfer. If we could just figure that out, we'd be fine.

            • 3. Re: Can't figure out DMA...
              EtWh_2921621

              I figured it out. The trouble was LPM. here's the situation, for others that get stumped:

              First off, know this: USB doesn't allow a USB device to sit there and "broadcast" packets. Packets are managed by a link state manager that expects the PC to send something across a control channel, then the link state manager allows transfers to happen for a SHORT period of time, then it will close off the link and further attempts at transmissions from the CX3 device to the PC, are banned. This is IF LPM is turned on. When you turn it off, and allow "infinite" transfers, the link state stays open.

               

              here's what was happening to me. First, I had LPM turned on. Next, I was sending a command from the PC to the CX3 to "read calibration data". the CX3 would read that command, then set an event, then read that event on a background thread, then read the NVM and get out the calibration data, then it would send that calibration data over DMA back to the PC. This would take so long, the LPM would kick in and the DMA transfer  was blocked on the PC's side. By turning the LPM off on the CX3, this fixed everything. Woah.

              • 4. Re: Can't figure out DMA...
                KandlaguntaR_36

                It is recommended to disable the LPM when you are doing the data transfer on USB 3.0 bus.