Forum Discussion

Altera_Forum's avatar
Altera_Forum
Icon for Honored Contributor rankHonored Contributor
20 years ago

SPI 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()

17 Replies