Altera_Forum
Honored Contributor
20 years agoSPI Slave bug?
Has anybody used the SPI core as a slave device in a NIOS II implementation? If so, have you had relatively frequent data errors?
There is no HAL for the SPI as slave, so I'm just using the IORD... and IOWR... macros defined in altera_avalon_spi_regs.h. I've tried both polling and ISR implementations, yet I keep getting data errors. I originally suspected noise on the SCLK or MOSI lines, but they look golden -- no glitches. Example: the SPI master sends 3 bytes -- 0xF1, 0x41, 0xF6. Most times, that's what I get, but over 10% of the time (!) I see examples such as: 0xF1 0x20 0xF6, or 0xF1 0xA8 0x81 0xF6 (and there are only 3 bytes sent, looking at the SCLK and MOSI lines), or 0xF1 0x41 0xBD. Here's my interrupt handler (the protocol stuff isn't relevant): void SpiSlave::InterruptHandler(void) { U_INT32 irq_flag_reg; U_INT32 irq_mask_reg; U_BYTE RxChar; static U_BYTE GotHeader = FALSE; IsrCount++; // read register to determine which flags are set irq_flag_reg = IORD_ALTERA_AVALON_SPI_STATUS(BaseAddress); // read the interrupt mask register irq_mask_reg = IORD_ALTERA_AVALON_SPI_CONTROL(BaseAddress); while (irq_flag_reg &= irq_mask_reg) { // Check for Rxed character if (irq_flag_reg & ALTERA_AVALON_SPI_STATUS_RRDY_MSK) { RxIsrCount++; if (RxMsgSize >= SPI_SLAVE_RX_BUFFER_SIZE) { // buffer overflow // queue an event RxMsgSize = 0; GotHeader = FALSE; } RxChar = IORD_ALTERA_AVALON_SPI_RXDATA(BaseAddress); if (GotHeader) { RxDataBuffer[RxMsgSize++] = RxChar; // store char in temp buffer if (RxChar == (U_BYTE)0xF6) { // ETX char received GotHeader = 0; DisableInterrupt(ALTERA_AVALON_SPI_CONTROL_ITRDY_MSK); // no more TRDY if (NewMsgRxed) { // app hasn't processed the last msg yet! // queue event } else { // ready to give msg to application // copy data to app buffer while (RxMsgSize--) { AppRxBuffer[RxMsgSize] = RxDataBuffer[RxMsgSize]; } NewMsgRxed = TRUE; RxMsgSize = 0; } } // end if ETX else { // not ETX if (RxChar > (U_BYTE)0xF0) { // unexpected header // start msg all over, beginning with the new header // note that GotHeader is already TRUE RxMsgSize = 0; RxDataBuffer[RxMsgSize++] = RxChar; } // end if unexpected header } // end if not ETX } // end if GotHeader else if ((RxChar > (U_BYTE)0xF0) && (RxChar != (U_BYTE)0xF6)) { // Header rxed GotHeader = TRUE; RxMsgSize = 0; RxDataBuffer[RxMsgSize++] = RxChar; } } // end if Rx Rdy interrupt if (irq_flag_reg & ALTERA_AVALON_SPI_STATUS_TRDY_MSK) { TxIsrCount++; if ((BytesToSend) && (TxData)) { // data to tx IOWR_ALTERA_AVALON_SPI_TXDATA(BaseAddress, *TxData++); BytesToSend--; } else { // nothing more to send, so disable ints to avoid infinite ints DisableInterrupt(ALTERA_AVALON_SPI_CONTROL_ITRDY_MSK); } } if (irq_flag_reg & ALTERA_AVALON_SPI_STATUS_ROE_MSK) { // Rx overrun error -- queue event? } if (irq_flag_reg & ALTERA_AVALON_SPI_STATUS_TOE_MSK) { // Tx overrun error -- queue event? } // re-read register to determine which flags are set irq_flag_reg = IORD_ALTERA_AVALON_SPI_STATUS(BaseAddress); // Clear status bits by writing anything to the status register //IOWR_ALTERA_AVALON_SPI_STATUS(BaseAddress, 0); // read the interrupt mask register irq_mask_reg = IORD_ALTERA_AVALON_SPI_CONTROL(BaseAddress); } // end while } // end SpiSlave::InterruptHandler()