Tool/software:
Hello,
I've been reading up on using the EPI peripheral and notice that there are use cases that do and do not use uDMA. I would like to know what the recommended approach is from TI. For background, my design utilizes TI RTOS. There is a tcp task, which receives commands through a POE interface, the EPI provides an interface from the MCU to a Lattice FPGA. The FPGA controls the state of some high-power relays, and there are interlocks for safety and other control signals.
Presently, I plan to use the EPI by creating a bunch a struct pointers for the "peripherals" within the FPGA, and then pointing these to addresses within the EPI's memory space. For example, here's a breakdown of a temperature sensor that the FPGA periodically polls through a SPI-like interface:
// Classes/Structures
/*Temperature Sensor Peripheral data structures */
/* Register Definitions */
typedef struct
{
//lsb
uint8_t enable : 1; ///< when set, the peripheral is enabled
uint8_t softRst : 1; ///< when set, the peripheral is held in reset
uint8_t modeSelection : 3; ///< per datasheet, selects the operational mode
uint8_t tempDataFormat : 3; ///< per datasheet, selects the temperature data format
//msb
}TempCntrlReg0Type;
typedef union
{
TempCntrlReg0Type bits;
uint8_t byte;
}TempCntrlReg0UseType;
typedef struct
{
//lsb
uint8_t faultQueue : 2; ///< per datasheet, holds fault related data/settings
uint8_t ctPinPolarity : 1; ///< per datasheet, sets the polarity of the CT pin
uint8_t intPinPolarity : 1; ///< per datasheet, sets the polarity of the INT pin
uint8_t intCtMode : 1; ///< per datasheet, sets the mode
uint8_t shutDown : 1; ///< per datasheet, sets the sensor into shutdown mode
uint8_t unused : 1; ///< unused bit
uint8_t resolution : 1; ///< per datasheet, sets the resolution
//msb
}TempCntrlReg1Type;
typedef union
{
TempCntrlReg1Type bits;
uint8_t byte;
}TempCntrlReg1UseType;
typedef struct
{
//lsb
uint8_t unused : 4; ///< unused bits
uint8_t tLow : 1; ///< per datasheet, enables the low temp. threshold
uint8_t tHigh : 1; ///< per datasheet, enables the high temp. threshold
uint8_t tCrt : 1; ///< per datasheet, enables the critical temp. threshold
uint8_t nRdy : 1; ////< per datasheet, the nRdy state
//msb
}TempStatusRegType;
typedef union
{
TempStatusRegType bits;
uint8_t byte;
}TempStatusRegUseType;
typedef struct
{
//lsb
uint16_t tempValue0 : 8; ///< lsb byte of two-byte value
uint16_t tempValue1 : 8; ///< msb byte of two-byte value
//msb
}TempValueRegType;
typedef union
{
TempValueRegType bytes;
uint16_t full;
}TempValueRegUseType;
typedef struct
{
//lsb
uint16_t tempCritValue0 : 8; ///< lsb byte of two-byte value
uint16_t tempCritValue1 : 8; ///< msb byte of two-byte value
//msb
}TempCritValueRegType;
typedef union
{
TempCritValueRegType bytes;
uint16_t full;
}TempCritValueRegUseType;
typedef struct
{
//lsb
uint8_t tHyst : 4;
uint8_t unused : 4;
}TempHysteresisRegType;
typedef union
{
TempHysteresisRegType bits;
uint8_t byte;
}TempHysteresisRegUseType;
typedef struct
{
//lsb
uint16_t tempHighValue0 : 8; ///< lsb byte of two-byte value
uint16_t tempHighValue1 : 8; ///< msb byte of two-byte value
//msb
}TempHighValueRegType;
typedef union
{
TempHighValueRegType bytes;
uint16_t full;
}TempHighValueRegUseType;
typedef struct
{
//lsb
uint16_t tempLowValue0 : 8; ///< lsb byte of two-byte value
uint16_t tempLowValue1 : 8; ///< msb byte of two-byte value
//msb
}TempLowValueRegType;
typedef union
{
TempLowValueRegType bytes;
uint16_t full;
}TempLowValueRegUseType;
/* Temp Sensor Peripheral Register Defintion */
typedef struct
{
//lsb
TempCntrlReg0UseType tempCntrlReg0;
TempCntrlReg1UseType tempCntrlReg1;
TempStatusRegUseType tempStatusReg;
TempValueRegUseType tempValueReg;
TempCritValueRegUseType tempCritValueReg;
TempHysteresisRegUseType tempHysterValueReg;
TempHighValueRegUseType tempHighValueReg;
TempLowValueRegUseType tempLowValueReg;
//msb
}TempSensorType;
Now, my plan is instantiate a pointer of this type and set it equal to the EPI base address + an offset specific for this peripheral.
TempSensorType* tempSense = EPI_BASE + TEMP_SENSE_ADDR (<- this is pseduo code meant to convey a concept and not an implementation).
When this is done, all I need to do is write or read from elements within this structure and resulting writes or reads will occur on the EPI. For example, if I wanted to enable this peripheral within the FPGA, my code would look like this:
tempSense->tempCntrlReg0.bits.enable = 1;
Equally true, if I wanted to read the value of the entire 8-bit register where the enable bit resides, it would like this this:
uint8_t cntrReg0Val = 0;
cntrlReg0Val = tempSense->tempCntrlReg0.byte;
There's going to be between 10 - 15 of these peripherals within the FPGA, and each of these will have structures (and structure pointers) used to write and read to specific address locations within the FPGA.
My question is would using uDMA be beneficial? I am concerned that these transactions my take too much time and other tasks, like the tcp task, could become less responsive.
And, if I do use uDMA would I have to use the EPI FIFO, or could memory-to-memory transfers be sufficient so that writing/reading to/from an element of a structure would result in a corresponding write/read to/from the EPI?
Thank you!