Our application receives blocks of 6,964 bytes of data streamed from an FPGA every 50ms. Our initial implementation used a 32-bit EPI data bus with a request-to-send signal sourced by the FPGA. The request-to-send signal triggers an interrupt on PortP:5, when triggered the interrupt reads the EPI bus data and writes it into a buffer. Once a full block of data is received, the block of data is transmitted using uDMA on SPI0. The problem we are experiencing is that servicing the EPI data interrupt starves out the main thread resulting in the USB receive/transmit to stop operating. Therefore, we have been attempting to migrate the code to use uDMA to transfer the data from the EPI bus.
Our initial attempt made the following assumptions:
1. Can't do uDMA transfer directly between EPI0 and SPI0. Therefore, our design uses uDMA to receive block of data from EPI bus into a buffer. When a full block of data has been received on the EPI bus, the data is transmitted using uDMA to SPI0 using received EPI buffer.
2. Need to use the EPI clock signal to synchronize EPI reads with the FPGA. The EPI clock uses EPI31, therefore we need to reduce the EPI bus size to 24, 16, or 8. The uDMA transactions are limited to data sizes of 32, 16, or 8. Therefore, our EPI data bus needs to be reduced to 16-bits.
3. Renamed the request-to-send signal sourced by the FPGA to frame signal. The frame signal rising edge triggers the PortP:5 interrupt to start the uDMA transaction to receive the data from the FPGA. The frame signal falling edge triggers PortP:5 which triggers the uDMA to stream the received data on SPI 0.
Please comment if any of these assumptions are incorrect or misunderstood.
We have the following implementation that is not operating, the problem is likely a result of our interpretation/understanding of the Tivaware documentation and examples. Will you please review the attached code blocks and provide guidance on how to use uDMA to transfer data from the EPI data bus.
#define CALC_EPI_DIVIDER( epiClkRate ) ( ROUND_D2UI(MASTER_CLOCK_FREQUENCY / epiClkRate) - 2 ) // Calculate EPI clock divider for requested EPI clock rate static void configEPIPeripheral( void ) { uint16_t divider = CALC_EPI_DIVIDER( 1.0E6 ); // Note : All ports enabled earlier // enablePeripheral( SYSCTL_PERIPH_GPIOX ); // Call routine to enable general purpose I/O on requested port // Note : uDMA enabled earlier // enablePeripheral( SYSCTL_PERIPH_UDMA ); // Enable the uDMA controller at the system level // MAP_uDMAEnable( ); // Enable the uDMA controller // MAP_uDMAControlBaseSet( m_uDMAControlTable ); // Point at the control table to use for channel control structures MAP_uDMAChannelSelectSecondary( UDMA_DEF_TMR1A_SEC_EPI0RX ); // Set default channel as EPI receive MAP_uDMAChannelAttributeDisable( UDMA_SEC_CHANNEL_EPI0RX, UDMA_ATTR_ALTSELECT | UDMA_ATTR_HIGH_PRIORITY | UDMA_ATTR_REQMASK ); // Put the attributes in a known state for the uDMA SSI0RX channel MAP_uDMAChannelAttributeEnable( UDMA_SEC_CHANNEL_EPI0RX, UDMA_ATTR_USEBURST ); // Enable use burst mode attribute MAP_uDMAChannelControlSet( UDMA_SEC_CHANNEL_EPI0RX | UDMA_PRI_SELECT, UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 | UDMA_ARB_8 ); // Configure the control parameters for the primary control structure for the SSI0 TX channel enablePeripheral( SYSCTL_PERIPH_EPI0 ); // Enable the EPI peripheral MAP_GPIOPinTypeEPI( GPIO_PORTA_BASE, PORT_A_EPI_PINS ); // Configure port A pins used for EPI MAP_GPIOPinConfigure( GPIO_PA6_EPI0S8 ); // Configure EPI data signal 8 MAP_GPIOPinConfigure( GPIO_PA7_EPI0S9 ); // Configure EPI data signal 9 // MAP_GPIOPinTypeEPI( GPIO_PORTB_BASE, PORT_B_EPI_PINS ); // Configure port B pins used for EPI // MAP_GPIOPinConfigure( GPIO_PB2_EPI0S27 ); // Configure EPI data signal 27 // MAP_GPIOPinConfigure( GPIO_PB3_EPI0S28 ); // Configure EPI data signal 28 MAP_GPIOPinTypeGPIOInput( GPIO_PORTB_BASE, PORT_B_EPI_PINS ); // Configure 16-bit EPI unused pins MAP_GPIOPinTypeEPI( GPIO_PORTC_BASE, PORT_C_EPI_PINS ); // Configure port C pins used for EPI MAP_GPIOPinConfigure( GPIO_PC4_EPI0S7 ); // Configure EPI data signal 7 MAP_GPIOPinConfigure( GPIO_PC5_EPI0S6 ); // Configure EPI data signal 6 MAP_GPIOPinConfigure( GPIO_PC6_EPI0S5 ); // Configure EPI data signal 5 MAP_GPIOPinConfigure( GPIO_PC7_EPI0S4 ); // Configure EPI data signal 4 MAP_GPIOPinTypeEPI( GPIO_PORTG_BASE, PORT_G_EPI_PINS ); // Configure port G pins used for EPI MAP_GPIOPinConfigure( GPIO_PG0_EPI0S11 ); // Configure EPI data signal 11 MAP_GPIOPinConfigure( GPIO_PG1_EPI0S10 ); // Configure EPI data signal 10 MAP_GPIOPinTypeEPI( GPIO_PORTH_BASE, PORT_H_EPI_PINS ); // Configure port H pins used for EPI MAP_GPIOPinConfigure( GPIO_PH0_EPI0S0 ); // Configure EPI data signal 0 MAP_GPIOPinConfigure( GPIO_PH1_EPI0S1 ); // Configure EPI data signal 1 MAP_GPIOPinConfigure( GPIO_PH2_EPI0S2 ); // Configure EPI data signal 2 MAP_GPIOPinConfigure( GPIO_PH3_EPI0S3 ); // Configure EPI data signal 3 MAP_GPIOPinTypeEPI( GPIO_PORTK_BASE, PORT_K_EPI_PINS ); // Configure port K pins used for EPI MAP_GPIOPinConfigure( GPIO_PK5_EPI0S31 ); // Configure EPI data signal 31 // MAP_GPIOPinConfigure( GPIO_PK6_EPI0S25 ); // Configure EPI data signal 25 // MAP_GPIOPinConfigure( GPIO_PK7_EPI0S24 ); // Configure EPI data signal 24 MAP_GPIOPinTypeGPIOInput( GPIO_PORTK_BASE, EPI0_SIGNAL_24_PK7 | EPI0_SIGNAL_25_PK6 ); // Configure 16-bit EPI unused pins // MAP_GPIOPinTypeEPI( GPIO_PORTL_BASE, PORT_L_EPI_PINS ); // Configure port L pins used for EPI // MAP_GPIOPinConfigure( GPIO_PL0_EPI0S16 ); // Configure EPI data signal 16 // MAP_GPIOPinConfigure( GPIO_PL1_EPI0S17 ); // Configure EPI data signal 17 // MAP_GPIOPinConfigure( GPIO_PL2_EPI0S18 ); // Configure EPI data signal 18 // MAP_GPIOPinConfigure( GPIO_PL3_EPI0S19 ); // Configure EPI data signal 19 // MAP_GPIOPinConfigure( GPIO_PL4_EPI0S26 ); // Configure EPI data signal 20 MAP_GPIOPinTypeGPIOInput( GPIO_PORTL_BASE, PORT_L_EPI_PINS ); // Configure 16-bit EPI unused pins MAP_GPIOPinTypeEPI( GPIO_PORTM_BASE, PORT_M_EPI_PINS ); // Configure port M pins used for EPI MAP_GPIOPinConfigure( GPIO_PM0_EPI0S15 ); // Configure EPI data signal 15 MAP_GPIOPinConfigure( GPIO_PM1_EPI0S14 ); // Configure EPI data signal 14 MAP_GPIOPinConfigure( GPIO_PM2_EPI0S13 ); // Configure EPI data signal 13 MAP_GPIOPinConfigure( GPIO_PM3_EPI0S12 ); // Configure EPI data signal 12 // MAP_GPIOPinTypeEPI( GPIO_PORTP_BASE, PORT_P_EPI_PINS ); // Configure port P pins used for EPI // MAP_GPIOPinConfigure( GPIO_PP2_EPI0S29 ); // Configure EPI data signal 29 // MAP_GPIOPinConfigure( GPIO_PP3_EPI0S30 ); // Configure EPI data signal 30 MAP_GPIOPinTypeGPIOInput( GPIO_PORTP_BASE, PORT_P_EPI_PINS ); // Configure 16-bit EPI unused pins // MAP_GPIOPinTypeEPI( GPIO_PORTQ_BASE, PORT_Q_EPI_PINS ); // Configure port Q pins used for EPI // MAP_GPIOPinConfigure( GPIO_PQ0_EPI0S20 ); // Configure EPI data signal 20 // MAP_GPIOPinConfigure( GPIO_PQ1_EPI0S21 ); // Configure EPI data signal 21 // MAP_GPIOPinConfigure( GPIO_PQ2_EPI0S22 ); // Configure EPI data signal 22 // MAP_GPIOPinConfigure( GPIO_PQ3_EPI0S23 ); // Configure EPI data signal 23 MAP_GPIOPinTypeGPIOInput( GPIO_PORTQ_BASE, PORT_Q_EPI_PINS ); // Configure 16-bit EPI unused pins MAP_EPIModeSet( EPI0_BASE, EPI_MODE_GENERAL ); // Enable read of EPI input data MAP_EPIDividerSet( EPI0_BASE, divider ); // Set EPI clock rate = 1MHz MAP_EPIConfigGPModeSet( EPI0_BASE, EPI_GPMODE_CLKPIN | EPI_GPMODE_CLKGATE | EPI_GPMODE_DSIZE_16, 0, 0 ); // No Address, 32 bit Data // MAP_EPIAddressMapSet( EPI0_BASE, EPI_ADDR_PER_BASE_A ); // Address map for 32-bit interrupt 0xA000.0000 MAP_EPIAddressMapSet( EPI0_BASE, EPI_ADDR_PER_BASE_NONE ); // Address map for 16-bit set to none MAP_EPINonBlockingReadConfigure( EPI0_BASE, 0, EPI_NBCONFIG_SIZE_16, 0 ); // Configure non-blocking EPI reads MAP_GPIOIntTypeSet( GPIO_PORTP_BASE, GPIO_PIN_5, GPIO_BOTH_EDGES ); // Configure frame interrupt trigger on both edges IntRegister( INT_GPIOP5, epiIntHandler ); // Register the requested interrupt handler MAP_GPIOIntClear( GPIO_PORTP_BASE, GPIO_PIN_5 ); // Clear interrupt before enabling MAP_GPIOIntEnable( GPIO_PORTP_BASE, GPIO_PIN_5 ); // Enable the requested GPIO interrupt MAP_IntEnable( INT_GPIOP5 ); // Enable requested interrupt } static void enablePeripheral( uint32_t peripheralId ) { MAP_SysCtlPeripheralEnable( peripheralId ); // Enable requested peripheral while( ! MAP_SysCtlPeripheralReady( peripheralId ) ); // Wait until the enabled peripheral is ready } void epiIntHandler( void ) { MAP_GPIOIntClear( GPIO_PORTP_BASE, GPIO_PIN_5 ); // Clear interrupt if( ! MAP_uDMAChannelIsEnabled( UDMA_SEC_CHANNEL_EPI0RX ) ) // If previous streaming data done { if( MAP_GPIOPinRead( GPIO_PORTP_BASE, GPIO_PIN_5 ) ) // If EPI frame rising edge { MAP_uDMAChannelTransferSet( UDMA_SEC_CHANNEL_EPI0RX, UDMA_MODE_BASIC, m_streamingData[ m_epiLoadPingPongInx ].spiAccess, (void *)(EPI0_BASE + EPI_O_READFIFO0), STREAMING_TOTAL_WORDS ); // Start DMA transfer from EPI0 RX MAP_uDMAChannelEnable( UDMA_SEC_CHANNEL_EPI0RX ); // Enabling channel wiil issue a transfer request and data transfer begins MAP_EPINonBlockingReadStart( EPI0_BASE, 0, STREAMING_TOTAL_WORDS ); // m_epiLoadPingPongInx = ! m_epiLoadPingPongInx; // Toggle the ping-pong index } else // Else, EPI frame fallsing edge m_epiDataRdy = true; // Set EPI data ready flag } else // Else, channel is still enabled (busy) m_badStreamingEPIReadCnt++; // Increment the bad EPI read counter }