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
}
