Forum Discussion
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
- Altera_Forum
Honored Contributor
I use the SOPC-Builder to build my project. You can find that module in the SOPC-Builder Library under "Interface Protocols" -> "Serial" -> "SPI (3 Wire Serial)".
Or, you may find it in your install directory: <root>/altera/<version>/ip/altera/sopc_builder_ip/altera_avalon_spi/ - Altera_Forum
Honored Contributor
Not the SPI (as in Serial Periperal Interface), I wrote SP1 (as in QII 9.1 Service Pack1). Where did you get it from ?
Update: Nevermind, I found it now. It was not announced in the front page as I expected it to be. - Altera_Forum
Honored Contributor
Ah, I missunderstood this.
Go to altera.com --> "Products" --> "Design Software" --> on the right side "Download Software" There you can choose to download the latest Version of QuartusII. It includes this SP1. I think there is no small SP1 installer, you need to install the whole QuartusII. - Altera_Forum
Honored Contributor
SOPC bilder generates, as seems, wrong verilog code for SPI slave, at SS_n rise edge rx register shifted one more time, and ROE bit is set to 1. To avoid this, you should read RX register before SS_n rise edge or delay SS_n edge, if it possible.
- Altera_Forum
Honored Contributor
--- Quote Start --- SOPC bilder generates, as seems, wrong verilog code for SPI slave, at SS_n rise edge rx register shifted one more time, and ROE bit is set to 1. To avoid this, you should read RX register before SS_n rise edge or delay SS_n edge, if it possible. --- Quote End --- Could you elaborate on this? Also, is Altera going to fix this bug? - Altera_Forum
Honored Contributor
I believe I found the solution to this problem. I had my SPI slave running from a 40MHz source. The data rate I was using was <1MHz. I found that the lower data rate I would run my master at, the worse these errors became. I changed my SPI slave to run at a 1MHz rate. The errors went away. So it seems that your SPI slave clock should be close to the data rate you intend to run at. I'm not sure how close it has to be or what the upper limit on the SPI slaves data rate is.
- Altera_Forum
Honored Contributor
Hi, you said if the clock rate over 1MHz the transmission error went away. I tried it, and it had a little effect, the errors reduced ,but sometimes it still came. Would you please provide some code for the slave and master program ?I just want to implement transmitting 32bit data from master side to slave one and then read back. I have paste some of my code and post it, the link is alteraforum.com/forum/showthread.php?t=36274
Thanks a lot!:)