6 Replies Latest reply on Mar 3, 2015 1:20 PM by hugh.urwin

    Terminating DMA Transaction/Chain

              Hello I am working in a Project involving a psoc3 as a SPI slave to a STM32 master. The Slave SPI component is linked to a DMA channel with 2 Transactions chained. The first Transaction receives an 8-byte command/arguments telegram and stores it in a buffer. The transaction is set to autoincrement destination, 1 byte burst and 1 byte count). Once the first transaction is done the next transaction in the chain starts and this one should run until the command requested in the telegram is executed. The first byte of this transaction (usually a 0x00 overwriting a 0xff) tells the psoc program loop (includes 4-channel data adquisition, capacitive keyboard scanning, etc) that there's a command from the master pending. Once the psoc program loop checks out this variable and finds it to be other than 0xff sends and ACK byte (0x21) to the master for it to clock out the amount of data that particular command requires. Since (before the ACK byte was sent by the psoc) the incoming bytes from the SPI are just meant for clocking the data out of the slave (psoc) this DMA transaction stores the rxd byte in the same location and since (both master and slave run diff tasks asynchronously) the number of clocking data out bytes is unknown the transaction number of bytes is set to 0 so it will run continuously until I stop it. Once it is stopped the DMA channel should start again the first transaction (the 8-byte long one) and the cycle repeats over and over again. Problem is that when I end the transaction with CyDmaChSetRequest( KAFE_SPI_DMA_Chan, CY_DMA_CPU_TERM_TD ); it fails to reset the destination pointer to the address it should but it usually does to the 8th byte of the buffer and after some cycles the DMA channel seems to stop responding.. What are exactly the conditions requiered for that request to be executed by the DMA channel? I even tried adding while(CyDmaChGetRequest( KAFE_SPI_DMA_Chan) ); after the CyDmaChSetRequest to wait until the command has been executed as stated in the DMA component data sheet but it made no difference. Does it need a call to another function for the DMA channel to start from the first transaction in the chain correctly? It doesn't seem to have to do with the SPI or DMA channel configuration because if i set the second transaction number of bytes to 1 and send 9 bytes from the SPI master everything works alright. I read in some other post (I don't know how reliable that info could actually be) that transactions set up with the bytes count set to zero are not reliable but the component Data Sheet states that that setup is acceptable. Regarding the DMA hardware termination input the component data sheet says that a leading edge in that input is ignored unless it is executing a burst. Would a high level in this input terminate the transaction when a new burst is executed or it's got to be always a pulse rising edge? --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- The relevant code for this implementation follows #ifdef DMA_SPI_APPROACH /* Variable declarations for KAFE_SPI_DMA - Writing a byte from SPI into comm buffer */ uint8 KAFE_SPI_DMA_Chan; uint8 KAFE_SPI_DMA_TD[2]; /* DMA Configuration for KAFE_SPI_DMA */ #define KAFE_SPI_DMA_BYTES_PER_BURST 1 #define KAFE_SPI_DMA_REQUEST_PER_BURST 1 #define KAFE_SPI_DMA_SRC_BASE (CYDEV_SRAM_BASE) #define KAFE_SPI_DMA_DST_BASE (CYDEV_SRAM_BASE) uint8 xdata spirxbuff[8]; uint8 xdata spirxbtrash[1]; uint8 cTID, DMAStat; #endif #ifdef DMA_SPI_APPROACH //--------------------------------------------------------------------------------------------------------------------------------------------------------------------- // SPI Slave Data into SPI Comnd buffer. Will receive 8 bytes from Master and store them in 8 consecutive memory // positions. After that the first transaction is ended and the second transaction will receive an unknown number // of bytes and store them in the same memory position. Main local program loop will check this memory position for // a given byte which means master is done sending command/arguments and is waiting/clocking for an ACK from slave to start // sending nulls and clocking out the data from slave. /* * The parameter Upper 16 bits of the Source and Destination address is specific to the device family * because the memory map of PSoC3 and PSoC5 changes. Hence the DMA Initialization will change based on * PSoC3 or PSoC5. In order to make the code compatible with both PSoC3 and PSoC5, the DMA * Initialization is done within preprocessor statement which checks for the compiler used. * PSoC3 uses Keil_C51 and it is defined using __C51__ * Else initialization for PSoC5 is used */ #if (defined(__C51__)) KAFE_SPI_DMA_Chan = KAFE_SPI_DMA_DmaInitialize(KAFE_SPI_DMA_BYTES_PER_BURST, KAFE_SPI_DMA_REQUEST_PER_BURST, HI16(KAFE_SPI_DMA_SRC_BASE), HI16(KAFE_SPI_DMA_DST_BASE) ); #else KAFE_SPI_DMA_Chan = KAFE_SPI_DMA_DmaInitialize(KAFE_SPI_DMA_BYTES_PER_BURST, KAFE_SPI_DMA_REQUEST_PER_BURST, HI16(KAFE_SPI_DMA_SRC_BASE), HI16((unit32)(&spirxbuff[0])) ); #endif KAFE_SPI_DMA_TD[0] = CyDmaTdAllocate(); KAFE_SPI_DMA_TD[1] = CyDmaTdAllocate(); CyDmaTdSetConfiguration(KAFE_SPI_DMA_TD[0], 8, KAFE_SPI_DMA_TD[1], KAFE_SPI_DMA__TD_TERMOUT_EN | TD_INC_DST_ADR); CyDmaTdSetConfiguration(KAFE_SPI_DMA_TD[1], 0, KAFE_SPI_DMA_TD[0], TD_TERMIN_EN | KAFE_SPI_DMA__TD_TERMOUT_EN); /* Set the source of TD_rx as SPIS_RXDATA_PTR Address and SPI slave Rx Buffer as the destination */ CyDmaTdSetAddress(KAFE_SPI_DMA_TD[0], LO16(((uint32)KAFE_SPIS_RXDATA_PTR)), LO16((uint32)&spirxbuff[0]) ); CyDmaTdSetAddress(KAFE_SPI_DMA_TD[1], LO16(((uint32)KAFE_SPIS_RXDATA_PTR)), LO16((uint32)&spirxbtrash[0]) ); CyDmaChSetInitialTd(KAFE_SPI_DMA_Chan, KAFE_SPI_DMA_TD[0]); spirxbtrash[0] = 0xff; /* Enable the SPI Rx DMA channel and preserve TDs */ CyDmaChEnable(KAFE_SPI_DMA_Chan, 1); #endif // this code is part of main program loop #ifdef DMA_SPI_APPROACH if( spirxbtrash[0] == 0x00 ) { // do we have a complete command telegram? //--- process command while( !(KAFE_SPIS_ReadTxStatus() & KAFE_SPIS_STS_BYTE_COMPLETE) ); KAFE_SPIS_WriteTxDataZero( AFE_CMND_ACK ); spirxbtrash[0] = 0xff; switch( spirxbuff[0] ) { // process command case AFE_SPI_IDLE: while( !(KAFE_SPIS_ReadTxStatus() & KAFE_SPIS_STS_SPI_DONE) ); KAFE_SPIS_WriteTxDataZero( 0x00 ); break; case AFE_DUMMYC: CyDmaChSetRequest( KAFE_SPI_DMA_Chan, CY_DMA_CPU_TERM_TD ); // while(CyDmaChGetRequest( KAFE_SPI_DMA_Chan) ); ; // CyDmaChSetRequest( KAFE_SPI_DMA_Chan, CY_DMA_CPU_REQ ); //CyDmaChEnable( KAFE_SPI_DMA_Chan, 1 ); // CyDmaChSetRequest( KAFE_SPI_DMA_Chan, CPU_REQ ); while( !(KAFE_SPIS_ReadTxStatus() & KAFE_SPIS_STS_SPI_DONE) ); KAFE_SPIS_ClearRxBuffer(); // KAFE_SPIS_WriteTxDataZero( 0x00 ); // DMA_Reg_Write( 0x01 ); // DMA_Reg_Write( 0x01 ); // DMA_Reg_Write( 0x00 ); // KAFE_SPIS_WriteTxDataZero( AFE_DUMMYC ); break; case AFE_SET_CAL_MODE: case AFE_END_CAL_MODE: case AFE_STO_CAL_DATA: // command to store calib data in psoc eeprom. break; case AFE_PING: // Alive Check and Reporting case AFE_PONG: // kept only for alignment case AFE_CAL_STAT: // enquiries Keyboard-Analog front end for calibration integrity case AFE_RESET: default: break; } /* switch spi_task_id */ //-- terminate DMA transaction once we're done clocking out the stuff while( !(KAFE_SPIS_ReadTxStatus() & KAFE_SPIS_STS_SPI_DONE) ); // KAFE_SPIS_WriteTxData( AFE_DUMMYC ); spirxbtrash[0] = 0xff; // we do not have a complete command telegram anymore CyDmaChSetRequest( KAFE_SPI_DMA_Chan, CPU_TERM_TD ); // while(CyDmaChGetRequest( KAFE_SPI_DMA_Chan) ); ; KAFE_SPIS_ClearRxBuffer(); // CyDmaChSetRequest( KAFE_SPI_DMA_Chan, CPU_REQ ); } #endif Thank you in advance Aattached is a capture of the related hardware schematic (nevermind the FF and the Stat/Ctrl registers...the signals are not being activated at all) and a capture of the logic analizar showing the incoming data and the SPI otput triggering the DMA channel.   
        • 1. Re: Terminating DMA Transaction/Chain

          The best way for forum to help is post a project so that all


          affected settings code can be examined -








          “File”                                                             Creator


          “Create Workspace Bundle”




          Regards, Dana.

          • 2. Re: Terminating DMA Transaction/Chain
                    For starters I should say sorry about the messy post. On google chrome the fórum software wouldn't let me set the focus to the editing field and on Internet Exploider the edit window is just two lines and twenty chars wide which is very annoying if you have to type a long post. To vercome that i edited the text properly on Notepad then pasted it into the text field with the results that are there for everyone to see. Anyways here is the corrected text   
            • 3. Re: Terminating DMA Transaction/Chain
                      And here is the hardware arrangement. Anyways, the question is just what are the conditions for the DMA channel to terminate a transaction with a call to CyDmaChSetRequest( KAFE_SPI_DMA_Chan, CPU_TERM_TD ); Will it terminate the transaction right upon the command is issued or does it need another burst for it to execute? As for the hardware termination input what happens if it goes hi while there is no burst in progress yet it is left high then another burst comes?   
              • 4. Re: Terminating DMA Transaction/Chain
                        Anyone from Cypress willing to support their product (which we adoptedfor new designs when it still was ES2)? Attached now is a simple Project to be run on CY8CKIT-001. The Project was adapted from EP56273 and shows that the function (at least with the documentation available for the same) CyDmaChSetRequest(channel_rx, CPU_TERM_TD); fails to perform as it is supposed to do. The code is profusely commented indicating what Works and what doesn't. Thank you.   
                • 5. Re: Terminating DMA Transaction/Chain

                  This is a developer forum and not a Cypress forum. Many of the solutions and answers are provided by us PSoC users.


                  If you want to get in contact with Cypress best will be to create a "MyCase".


                  Now to your problem: The line


                      #if (defined(__C51__))
                          channel_rx = DMA_Rx_DmaInitialize(1,1, 0, 0);


                  contains a bad upper address (zero), look into the DMA component's datasheet. Use CYDEV_PERIPH_BASE and CYDEV_SRAM_BASE instead.





                  • 6. Re: Terminating DMA Transaction/Chain
                            My bad then. I actually thought that it was some kind of oficial fórum and that was actively monitored by Cypress employees. Regarding the ssubject that's not the problem because those bytes are the higher word of a 32-bit address which are always 0 in the 16-bit psoc address space. They're actually defined as 0 in cydevice.h (well CYDEV_PERIPH_BASE is different from 0 but the SPI component registers or the pointers to them seem to be mapped in the memory space). The cypress example used 0,0. All that regardless of the fact that the project is working alright if the DMA transactions complete the fixed and known in anticipation number of bytes. The problem arises when it comes to terminate the transaction or the chain under software control with the function provided in the component's API. Terminating a TD will fail miserably while terminating the chain Works "alright" but it surely imposes a penalty in run time. I've been doing more tests after uploading the Project and terminating the chain with the ad-hoc function fails too if you have more tan one TD in the chain.... you need to not only restart the channel but set the initial TD again before it for the scheme to work properly. Thank you for your input. I guess I'll have to take the problema as a case.