/* * FPGA.c * * Created on: Aug 15, 2024 * Author: fw */ #include // For memcpy and memset #include "sci.h" #include "sys_dma.h" #include "FPGA.h" #include "Utilities.h" #define FPGA_PAGE_SIZE 256 ///< FPGA register pages are 256 bytes. #define FPGA_EXPECTED_ID 0x5A ///< Expected ID for HD FPGA. #define FPGA_WRITE_CMD_BUFFER_LEN ( FPGA_PAGE_SIZE + 8 ) ///< FPGA write command buffer byte length. #define FPGA_READ_CMD_BUFFER_LEN 8 ///< FPGA read command buffer byte length. #define FPGA_WRITE_RSP_BUFFER_LEN 8 ///< FPGA write command response buffer byte length. #define FPGA_READ_RSP_BUFFER_LEN ( FPGA_PAGE_SIZE + 8 ) ///< FPGA read command response buffer byte length. #define SCI2_RECEIVE_DMA_REQUEST 28 ///< Serial port 2 receive DMA request line. #define SCI2_TRANSMIT_DMA_REQUEST 29 ///< Serial port 2 transmit DMA request line. #define GET_LSB_OF_WORD(w) ((U08)((w) & MASK_OFF_MSB)) ///< Macro returns the least signficant byte of a 2-byte word #define GET_MSB_OF_WORD(w) ((U08)(((w) >> SHIFT_8_BITS_FOR_BYTE_SHIFT) & MASK_OFF_MSB)) ///< Macro returns the most signficant byte of a 2-byte word #define MAKE_WORD_OF_BYTES(h, l) ((((U16)(h) << SHIFT_8_BITS_FOR_BYTE_SHIFT) & MASK_OFF_LSB) | ((U16)(l) & MASK_OFF_MSB)) ///< Macro merges two bytes into a 2-byte word #define FPGA_READ_CMD_CODE 0x5A ///< FPGA read command code. #define FPGA_HEADER_START_ADDR 0x0000 ///< Start address for FPGA header data. #define FPGA_READ_CMD_ACK 0xAA ///< FPGA read command ACK code. #define FPGA_READ_CMD_HDR_LEN 4 ///< FPGA read command header byte length. #define FPGA_READ_RSP_HDR_LEN 3 ///< FPGA read command response header byte length. #define SCI_DMA_TRANSMIT_INT 0x00010000 ///< Bit mask for setting/clearing serial DMA transmit interrupts. #define SCI_DMA_RECEIVE_INT 0x00060000 ///< Bit mask for setting/clearing serial DMA receive interrupts. typedef enum { FPGA_COMM_IDLE = 0, FPGA_COMM_READ_IN_PROGRESS, FPGA_COMM_READ_RESP_RECEIVED, FPGA_COMM_WRITE_IN_PROGRESS, FPGA_COMM_WRITE_RESP_RECEIVED, NUM_OF_FPGA_COMM_STATUS } FPGA_COMM_STATE_T; typedef enum { FPGA_START_STATE = 0, FPGA_RQST_HEADER_STATE, FPGA_RCV_HEADER_STATE, FPGA_WRITE_TO_FPGA_STATE, FPGA_READ_FROM_FPGA_STATE, FPGA_IDLE_STATE, NUM_OF_FPGA_STATES } FPGA_STATE_T; typedef struct { FPGA_COMM_STATE_T fpgaCommRead; FPGA_COMM_STATE_T fpgaCommWrite; } FPGA_COMM_STATUS_T; // FPGA Sensors Record #pragma pack(push,1) /// Record structure for FPGA header read. typedef struct { U08 fpgaId; ///< Reg 0. FPGA ID code. Checked against expected value at power up to verify basic FPGA communication and operation. U08 fpgaRev; ///< Reg 1. FPGA revision (minor) being reported. U08 fpgaRevMajor; ///< Reg 2. FPGA revision (major) being reported. U08 fpgaRevLab; ///< Reg 3. FPGA revision (lab) being reported. U16 fpgaStatus; ///< Reg 4. FPGA status register. } FPGA_HEADER_T; // Read only on FPGA #pragma pack(pop) // FPGA comm buffers static U08 fpgaWriteCmdBuffer[ FPGA_WRITE_CMD_BUFFER_LEN ]; ///< FPGA write command buffer. Holds the next FPGA write command to be transmitted. static U08 fpgaReadCmdBuffer[ FPGA_READ_CMD_BUFFER_LEN ]; ///< FPGA read command buffer. Holds the next FPGA read command to be transmitted. static U08 fpgaWriteResponseBuffer[ FPGA_WRITE_RSP_BUFFER_LEN ]; ///< FPGA write command response buffer. Memory reserved to capture the response to the last FPGA write command. static U08 fpgaReadResponseBuffer[ FPGA_READ_RSP_BUFFER_LEN ]; ///< FPGA read command response buffer. Memory reserved to capture the response to the last FPGA read command. // DMA control records static g_dmaCTRL fpgaDMAWriteControlRecord; ///< DMA record for controlling a DMA write command transmission from buffer. static g_dmaCTRL fpgaDMAWriteRespControlRecord; ///< DMA record for controlling a DMA write command reception to buffer. static g_dmaCTRL fpgaDMAReadControlRecord; ///< DMA record for controlling a DMA read command transmission from buffer. static g_dmaCTRL fpgaDMAReadRespControlRecord; ///< DMA record for controlling a DMA read command reception to buffer. static FPGA_HEADER_T fpgaHeader; ///< Record of last received FPGA header data. static FPGA_STATE_T fpgaState; static FPGA_COMM_STATUS_T fpgaCommStatus; static void initDMA( void ); static void consumeUnexpectedData( void ); static void setSCI2DMAReceiveInterrupt( void ); static void setSCI2DMATransmitInterrupt( void ); static void setupDMAForReadResp( U32 bytes2Receive ); static void setupDMAForReadCmd( U32 bytes2Transmit ); static void startDMAReceiptOfReadResp( void ); static void startDMAReadCmd( void ); static void resetFPGACommFlags( void ); static FPGA_STATE_T handleFPGARequestHeaderState( void ); static FPGA_STATE_T handleFPGARecieveHeaderState( void ); static FPGA_STATE_T handleFPGAWriteToFPGAState( void ); static FPGA_STATE_T handleFPGAReadFromFPGAState( void ); static FPGA_STATE_T handleFPGAIdleState( void ); void initFPGA( void ) { memset( &fpgaHeader, 0x0, sizeof( FPGA_HEADER_T ) ); initDMA(); consumeUnexpectedData(); resetFPGACommFlags(); fpgaState = FPGA_START_STATE; } void execFPGA( void ) { switch( fpgaState ) { case FPGA_START_STATE: fpgaState = FPGA_RQST_HEADER_STATE; // TODO this needs to be changed read from FPGA state to first see if there is an update or not. This is for test break; case FPGA_RQST_HEADER_STATE: fpgaState = handleFPGARequestHeaderState(); break; case FPGA_RCV_HEADER_STATE: fpgaState = handleFPGARecieveHeaderState(); break; case FPGA_WRITE_TO_FPGA_STATE: fpgaState = handleFPGAWriteToFPGAState(); break; case FPGA_READ_FROM_FPGA_STATE: fpgaState = handleFPGAReadFromFPGAState(); break; case FPGA_IDLE_STATE: fpgaState = handleFPGAIdleState(); break; default: // Do nothing break; } } void signalFPGAReceiptCompleted( void ) { // TODO What is the purpose of fpgaReceiptCounter++ in firmware? if ( FPGA_COMM_WRITE_IN_PROGRESS == fpgaCommStatus.fpgaCommWrite ) { fpgaCommStatus.fpgaCommWrite = FPGA_COMM_WRITE_RESP_RECEIVED; } else if ( FPGA_COMM_READ_IN_PROGRESS == fpgaCommStatus.fpgaCommRead ) { fpgaCommStatus.fpgaCommRead = FPGA_COMM_READ_RESP_RECEIVED; } } void clearSCI2DMAReceiveInterrupt( void ) { scilinREG->CLEARINT = SCI_DMA_RECEIVE_INT; } /*********************************************************************//** * @brief * The clearSCI2DMATransmitInterrupt function disables DMA transmit interrupts * for the SCI2 peripheral. * @details Inputs: none * @details Outputs: DMA transmit interrupt is disabled. * @return none *************************************************************************/ void clearSCI2DMATransmitInterrupt( void ) { scilinREG->CLEARINT = SCI_DMA_TRANSMIT_INT; } static void initDMA( void ) { // Enable interrupt notifications for FPGA serial port sciEnableNotification( scilinREG, SCI_OE_INT | SCI_FE_INT ); // Assign DMA channels to h/w DMA requests dmaReqAssign( DMA_CH0, SCI2_RECEIVE_DMA_REQUEST ); dmaReqAssign( DMA_CH2, SCI2_TRANSMIT_DMA_REQUEST ); // Set DMA channel priorities dmaSetPriority( DMA_CH0, HIGHPRIORITY ); dmaSetPriority( DMA_CH2, LOWPRIORITY ); // Enable DMA block transfer complete interrupts dmaEnableInterrupt( DMA_CH0, BTC ); dmaEnableInterrupt( DMA_CH2, BTC ); // Initialize FPGA DMA Write Control Record fpgaDMAWriteControlRecord.PORTASGN = 4; // Port B (only choice per datasheet) fpgaDMAWriteControlRecord.SADD = (U32)fpgaWriteCmdBuffer; // Transfer source address fpgaDMAWriteControlRecord.DADD = (U32)(&(scilinREG->TD)); // Dest. is SCI2 xmit register fpgaDMAWriteControlRecord.CHCTRL = 0; // No chaining fpgaDMAWriteControlRecord.ELCNT = 1; // Frame is 1 element fpgaDMAWriteControlRecord.FRCNT = 0; // Block is TBD frames - will be populated later when known fpgaDMAWriteControlRecord.RDSIZE = ACCESS_8_BIT; // Element size is 1 byte fpgaDMAWriteControlRecord.WRSIZE = ACCESS_8_BIT; // fpgaDMAWriteControlRecord.TTYPE = FRAME_TRANSFER; // Transfer type is block transfer fpgaDMAWriteControlRecord.ADDMODERD = ADDR_INC1; // Source addressing mode is post-increment fpgaDMAWriteControlRecord.ADDMODEWR = ADDR_FIXED; // Dest. addressing mode is fixed fpgaDMAWriteControlRecord.AUTOINIT = AUTOINIT_OFF; // Auto-init off fpgaDMAWriteControlRecord.ELSOFFSET = 0; // Not used fpgaDMAWriteControlRecord.ELDOFFSET = 0; // Not used fpgaDMAWriteControlRecord.FRSOFFSET = 0; // Not used fpgaDMAWriteControlRecord.FRDOFFSET = 0; // Not used // Initialize FPGA DMA Write Response Control Record fpgaDMAWriteRespControlRecord.PORTASGN = 4; // Port B (only choice per datasheet) fpgaDMAWriteRespControlRecord.SADD = (U32)(&(scilinREG->RD)); // Source is SCI2 recv register fpgaDMAWriteRespControlRecord.DADD = (U32)fpgaWriteResponseBuffer; // Transfer destination address fpgaDMAWriteRespControlRecord.CHCTRL = 0; // No chaining fpgaDMAWriteRespControlRecord.ELCNT = 1; // Frame is 1 element fpgaDMAWriteRespControlRecord.FRCNT = 0; // Block is TBD frames - will be populated later when known fpgaDMAWriteRespControlRecord.RDSIZE = ACCESS_8_BIT; // Element size is 1 byte fpgaDMAWriteRespControlRecord.WRSIZE = ACCESS_8_BIT; // fpgaDMAWriteRespControlRecord.TTYPE = FRAME_TRANSFER; // Transfer type is block transfer fpgaDMAWriteRespControlRecord.ADDMODERD = ADDR_FIXED; // Source addressing mode is fixed fpgaDMAWriteRespControlRecord.ADDMODEWR = ADDR_INC1; // Dest. addressing mode is post-increment fpgaDMAWriteRespControlRecord.AUTOINIT = AUTOINIT_OFF; // Auto-init off fpgaDMAWriteRespControlRecord.ELDOFFSET = 0; // Not used fpgaDMAWriteRespControlRecord.ELSOFFSET = 0; // Not used fpgaDMAWriteRespControlRecord.FRDOFFSET = 0; // Not used fpgaDMAWriteRespControlRecord.FRSOFFSET = 0; // Not used // Initialize FPGA DMA Read Control Record fpgaDMAReadControlRecord.PORTASGN = 4; // Port B (only choice per datasheet) fpgaDMAReadControlRecord.SADD = (U32)fpgaReadCmdBuffer; // Transfer source address fpgaDMAReadControlRecord.DADD = (U32)(&(scilinREG->TD)); // Dest. is SCI2 xmit register fpgaDMAReadControlRecord.CHCTRL = 0; // No chaining fpgaDMAReadControlRecord.ELCNT = 1; // Frame is 1 element fpgaDMAReadControlRecord.FRCNT = 0; // Block is TBD frames - will be populated later when known fpgaDMAReadControlRecord.RDSIZE = ACCESS_8_BIT; // Element size is 1 byte fpgaDMAReadControlRecord.WRSIZE = ACCESS_8_BIT; // fpgaDMAReadControlRecord.TTYPE = FRAME_TRANSFER; // Transfer type is block transfer fpgaDMAReadControlRecord.ADDMODERD = ADDR_INC1; // Source addressing mode is post-increment fpgaDMAReadControlRecord.ADDMODEWR = ADDR_FIXED; // Dest. addressing mode is fixed fpgaDMAReadControlRecord.AUTOINIT = AUTOINIT_OFF; // Auto-init off fpgaDMAReadControlRecord.ELSOFFSET = 0; // Not used fpgaDMAReadControlRecord.ELDOFFSET = 0; // Not used fpgaDMAReadControlRecord.FRSOFFSET = 0; // Not used fpgaDMAReadControlRecord.FRDOFFSET = 0; // Not used // Initialize FPGA DMA Read Response Control Record fpgaDMAReadRespControlRecord.PORTASGN = 4; // Port B (only choice per datasheet) fpgaDMAReadRespControlRecord.SADD = (U32)(&(scilinREG->RD)); // Source is SCI2 recv register fpgaDMAReadRespControlRecord.DADD = (U32)fpgaReadResponseBuffer; // Transfer destination address fpgaDMAReadRespControlRecord.CHCTRL = 0; // No chaining fpgaDMAReadRespControlRecord.ELCNT = 1; // Frame is 1 element fpgaDMAReadRespControlRecord.FRCNT = 0; // Block is TBD frames - will be populated later when known fpgaDMAReadRespControlRecord.RDSIZE = ACCESS_8_BIT; // Element size is 1 byte fpgaDMAReadRespControlRecord.WRSIZE = ACCESS_8_BIT; // fpgaDMAReadRespControlRecord.TTYPE = FRAME_TRANSFER; // Transfer type is block transfer fpgaDMAReadRespControlRecord.ADDMODERD = ADDR_FIXED; // Source addressing mode is fixed fpgaDMAReadRespControlRecord.ADDMODEWR = ADDR_INC1; // Dest. addressing mode is post-increment fpgaDMAReadRespControlRecord.AUTOINIT = AUTOINIT_OFF; // Auto-init off fpgaDMAReadRespControlRecord.ELDOFFSET = 0; // Not used fpgaDMAReadRespControlRecord.ELSOFFSET = 0; // Not used fpgaDMAReadRespControlRecord.FRDOFFSET = 0; // Not used fpgaDMAReadRespControlRecord.FRSOFFSET = 0; // Not used } static void consumeUnexpectedData( void ) { // Clear any errors sciRxError( scilinREG ); // If a byte is pending read, read it if ( sciIsRxReady( scilinREG ) != 0 ) { sciReceiveByte( scilinREG ); } } static void setSCI2DMAReceiveInterrupt( void ) { scilinREG->SETINT = SCI_DMA_RECEIVE_INT; } void setSCI2DMATransmitInterrupt( void ) { scilinREG->SETINT = SCI_DMA_TRANSMIT_INT; } static void setupDMAForReadResp( U32 bytes2Receive ) { // Verify # of bytes does not exceed buffer length if ( bytes2Receive <= FPGA_READ_RSP_BUFFER_LEN ) { fpgaDMAReadRespControlRecord.FRCNT = bytes2Receive; } } static void setupDMAForReadCmd( U32 bytes2Transmit ) { // Verify # of bytes does not exceed buffer length if ( bytes2Transmit <= FPGA_READ_CMD_BUFFER_LEN ) { fpgaDMAReadControlRecord.FRCNT = bytes2Transmit; } } static void startDMAReceiptOfReadResp( void ) { dmaSetCtrlPacket( DMA_CH0, fpgaDMAReadRespControlRecord ); dmaSetChEnable( DMA_CH0, DMA_HW ); setSCI2DMAReceiveInterrupt(); } static void startDMAReadCmd( void ) { dmaSetCtrlPacket( DMA_CH2, fpgaDMAReadControlRecord ); dmaSetChEnable( DMA_CH2, DMA_HW ); setSCI2DMATransmitInterrupt(); } static void resetFPGACommFlags( void ) { memset( &fpgaCommStatus, 0x0, sizeof( FPGA_COMM_STATUS_T ) ); } static FPGA_STATE_T handleFPGARequestHeaderState( void ) { FPGA_STATE_T state = FPGA_RCV_HEADER_STATE; U16 crc = 0; // Construct read command to read 3 registers starting at address 0 fpgaReadCmdBuffer[ 0 ] = FPGA_READ_CMD_CODE; fpgaReadCmdBuffer[ 1 ] = GET_LSB_OF_WORD( FPGA_HEADER_START_ADDR ); fpgaReadCmdBuffer[ 2 ] = GET_MSB_OF_WORD( FPGA_HEADER_START_ADDR ); fpgaReadCmdBuffer[ 3 ] = sizeof( FPGA_HEADER_T ); crc = crc16( fpgaReadCmdBuffer, FPGA_READ_CMD_HDR_LEN ); fpgaReadCmdBuffer[ 4 ] = GET_MSB_OF_WORD( crc ); fpgaReadCmdBuffer[ 5 ] = GET_LSB_OF_WORD( crc ); // Prep DMA for sending the read cmd and receiving the response fpgaCommStatus.fpgaCommRead = FPGA_COMM_READ_IN_PROGRESS; setupDMAForReadResp( FPGA_READ_RSP_HDR_LEN + sizeof( FPGA_HEADER_T ) + sizeof( U16 ) ); setupDMAForReadCmd( FPGA_READ_CMD_HDR_LEN + sizeof( U16 ) ); startDMAReceiptOfReadResp(); startDMAReadCmd(); return state; } static FPGA_STATE_T handleFPGARecieveHeaderState( void ) { FPGA_STATE_T state = FPGA_RCV_HEADER_STATE; if ( FPGA_COMM_READ_RESP_RECEIVED == fpgaCommStatus.fpgaCommRead ) { if ( FPGA_READ_CMD_ACK == fpgaReadResponseBuffer[ 0 ] ) { U32 rspSize = FPGA_READ_RSP_HDR_LEN + sizeof( FPGA_HEADER_T ); U32 crcPos = rspSize; U16 crc = MAKE_WORD_OF_BYTES( fpgaReadResponseBuffer[ crcPos ], fpgaReadResponseBuffer[ crcPos + 1 ] ); // Does the FPGA response CRC check out? if ( crc == crc16( fpgaReadResponseBuffer, rspSize ) ) { // Capture the read values memcpy( &fpgaHeader, &fpgaReadResponseBuffer[ FPGA_READ_RSP_HDR_LEN ], sizeof( FPGA_HEADER_T ) ); state = FPGA_IDLE_STATE; } } } resetFPGACommFlags(); // Should not be any data received at this time consumeUnexpectedData(); return state; } static FPGA_STATE_T handleFPGAWriteToFPGAState( void ) { FPGA_STATE_T state = FPGA_WRITE_TO_FPGA_STATE; return state; } static FPGA_STATE_T handleFPGAReadFromFPGAState( void ) { FPGA_STATE_T state = FPGA_READ_FROM_FPGA_STATE; // Should not be any data received at this time consumeUnexpectedData(); return state; } static FPGA_STATE_T handleFPGAIdleState( void ) { FPGA_STATE_T state = FPGA_IDLE_STATE; return state; }