1 Reply Latest reply on Jul 17, 2014 10:59 PM by user_193124556

    DMA from CAN

    user_193124556

      I'm having some trouble getting DMA from the CAN controller working.

         

      I am trying to measure latency so I am using a capture module to grab the time an interrupt occurs along with the DMA message itself and transfer it to a RAM buffer. When the buffer is filled I transfer the data to a PC over USB.

         

       

         

      I have the interrupt line from the CAN module connected to a synchronizer, which is then connected to the capture input of a timer. The capture output of the timer goes to the drq of the first of two DMA modules. The nrq from that DMA module goes to the drq of the second DMA module, and the nrq of that goes to an interrupt component:

         

       

         

         

      I found I needed a synchronizer between the CAN interrupt line and the caputre input of the timer module or I would get complaints about an async path.

         

       

         

      What I am seeing is that the code for the CANIRQ component is firing, but if I look at the RAM buffer, I do not see the data that should be there. No matter how many times I get a CAN message, the DMA destination buffer is static.

         

       

         

      The "CAPDMA" component is initialized such that it transfers 4 bytes from CanCapture_CAPTURE_LSB_PTR to the "capture time" are in the RAM buffer. Memory addresses are not incremented. Similarly, the CANDMA component is initialized to transfer the entire CAN_RX_STRUCT blob from CAN_RX[0] to the "CAN Data" area of the RAM buffer.

         

       

         

      My DMA init code is as follows:

         

       

         
      struct usb_rxcanmsg { uint32_t flags;   /* flags (arbitrary, set by this CPU) */ uint32_t captime;  /* capture time */ CAN_RX_STRUCT msg;  /* received message */ } __attribute__((packed));  static volatile struct usb_rxcanmsg rxcanmsg;  /* initializes the CAPDMA and CANDMA peripherals */ void dma_init(void) { uint8 dc, td;  /* DMA Configuration for CAPDMA */ #define CAPDMA_BYTES_PER_BURST 4 #define CANDMA_BYTES_PER_BURST (sizeof(CAN_RX_STRUCT))  /* transfers the capture count to the staging buffer */ dc = CAPDMA_DmaInitialize(CAPDMA_BYTES_PER_BURST, 1, HI16(CYDEV_SRAM_BASE), HI16(CYDEV_SRAM_BASE)); td = CyDmaTdAllocate(); CyDmaTdSetConfiguration(td, 4, td, CAPDMA__TD_TERMOUT_EN | TD_AUTO_EXEC_NEXT); CyDmaTdSetAddress(td, LO16((uint32)CanCapture_CAPTURE_LSB_PTR), LO16((uint32)&rxcanmsg.captime)); CyDmaChSetInitialTd(dc, td); CyDmaChEnable(dc, 1);  /* transfers the entire CAN_RX[0] structure to the staging buffer */ dc = CANDMA_DmaInitialize(CANDMA_BYTES_PER_BURST, 1, HI16(CYDEV_SRAM_BASE), HI16(CYDEV_SRAM_BASE)); td = CyDmaTdAllocate(); CyDmaTdSetConfiguration(td, sizeof(CAN_RX_STRUCT), td, CANDMA__TD_TERMOUT_EN); CyDmaTdSetAddress(td, LO16((uint32)&CAN_RX[0]), LO16((uint32)&rxcanmsg.msg)); CyDmaChSetInitialTd(dc, td); CyDmaChEnable(dc, 1); }
         
              
         
          I've verified with the debugger that there are no errors in the DMA initialization and that I have valid DMA descriptors and transfer descriptors.   
         
              
         
              
         
          My CANIRQ routine calls CAN_ReceiveMsg(0) and also explicitly clears the RX interrupt flag:   
         
              
         
              
         
          
      void SendUSBMsg(void)  {  //USB_LoadInEP(IN_EP, (uint8_t *)&rxcanmsg, sizeof(rxcanmsg));  CAN_ReceiveMsg(0);  CAN_INT_SR_REG.byte[1u] = CAN_RX_MESSAGE_MASK;  showmsg = true;  }
          
                
          
                
          

      I can see with the debugger that this routine is getting called, but when I look at the rxcanmsg data area the data does not match the CAN_RX[0] data area, nor does the capture time data value resemble anything like the current CanCapture timer value.

          

       

          

      Is there anything I have to do to manually acknowledge the DMA module transfers in my CANIRQ interrupt service routine? I'm at a bit of a loss here.

          

       

         
        • 1. Re: DMA from CAN
          user_193124556

          Got it figured out. It was just bad DMA configuration.  The correct DMA configuration routine is as follows:

             

           

             

          void dma_init(void)

             

          {

             

          uint8 dc, td;

             

           

             

          /* DMA Configuration for CAPDMA */

             

          #define CAPDMA_BYTES_PER_BURST (0)

             

          #define CANDMA_BYTES_PER_BURST (sizeof(CAN_RX_STRUCT))

             

           

             

          /* transfers the capture count to the staging buffer */

             

          dc = CAPDMA_DmaInitialize(CAPDMA_BYTES_PER_BURST, 0, HI16(CYDEV_UWRK_UWRK8_B1_BASE), HI16(CYDEV_SRAM_BASE));

             

          td = CyDmaTdAllocate();

             

          CyDmaTdSetConfiguration(td, 4, td, CAPDMA__TD_TERMOUT_EN | CY_DMA_TD_INC_SRC_ADR | CY_DMA_TD_INC_DST_ADR);

             

          CyDmaTdSetAddress(td, LO16((uint32)CanCapture_CAPTURE_LSB_PTR), LO16((uint32)&rxcanmsg.captime));

             

          CyDmaChSetInitialTd(dc, td);

             

          CyDmaChEnable(dc, 1);

             

           

             

          /* transfers the entire CAN_RX[0] structure to the staging buffer */

             

          dc = CANDMA_DmaInitialize(CANDMA_BYTES_PER_BURST, 0, HI16(CYDEV_CAN0_BASE), HI16(CYDEV_SRAM_BASE));

             

          td = CyDmaTdAllocate();

             

          CyDmaTdSetConfiguration(td, sizeof(CAN_RX_STRUCT), td, CANDMA__TD_TERMOUT_EN | CY_DMA_TD_INC_SRC_ADR | CY_DMA_TD_INC_DST_ADR);

             

          CyDmaTdSetAddress(td, LO16((uint32)&CAN_RX[0]), LO16((uint32)&rxcanmsg.msg));

             

          CyDmaChSetInitialTd(dc, td);

             

          CyDmaChEnable(dc, 1);

             

          }

             

           

             

          The errors:

             
                
          • Burst size should have been left at zero
          •     
          • src/dst addresses should increment (DMA does things in bytes, so even copying a dword (4 bytes) should increment
          •    
             

           

             

          Beyond that, I also had the base addresses for the CAN data and UDB based timer wrong, so even with those two items above fixed, I was still transferring incorrect data. I'll be posting another question here regarding UDB register base addresses in the next few minutes.

             

           

             

          I'm still curious about the CAN interrupt line requiring a synchronizer; it's not mentioned that the CAN interrupt line is async to the bus clock. Is this an omission or am I doing something wrong? My 16MHz clock is synchronized to the 64MHz bus clock. I didn't want such a small capture period which is why I have a 16MHz clock running the timer.