# SPI readings are shifted, inconsistent (nRF Master, FPGA Slave)

I'll try to simplify my project as much as possible to make this understandable. I'm wiring an nRF52 PCA10040 board to an iCE5LP (Lattice) FPGA.

I'm having an issue with the bytes coming in to the MCU (MISO line), here's the course of action:

1. Master sends command through SPI
2. Slave executes the command, and prepare data for transfer
3. After waiting long enough for the command to be processed and the data to be prepared, the Master sends dummy bytes to receive the tx register from the FPGA.
4. Data looks shifted, and is inconsistent.

The data is properly loaded, according to the simulation. What I receive every other transfer is the byte I'm expecting but shifted by one bit. See the following screenshots;

This is the simulation: This is the output from the logic analyzer: What I expect: (it's an eMMC initialization procedure)

• 0xC0FF8080 or 0x40FF8080 depending on the init result

What I get randomly:

0xE07FC040 which is 0xC0FF8080 >> 1 or 0x007F8040 which is 0x00FF0080 >> 1

static uint8_t       m_rx_buf[4];        /**< RX buffer. */
static uint8_t       m_rx_buf_ext[6];    /**< RX buffer. */
static uint8_t       m_tx_buf[] = {0x00, 0xaa, 0xbb, 0xcc};           /**< TX buffer. */

/**
* @brief SPI user event handler.
* @param event
*/
void spi_event_handler(nrf_drv_spi_evt_t const * p_event) {
spi_xfer_done = true;
NRF_LOG_PRINTF("Received: %x %x %x %x\n", m_rx_buf[0], m_rx_buf[1], m_rx_buf[2], m_rx_buf[3]);
}

/**
* Function to send a command to a specific module.
* @param module  : Module to communicate with
* @param command : Command to send
*/
ret_code_t sendCommand(uint8_t module, uint8_t command, uint8_t mmc_command, uint32_t mmc_arg) {
ret_code_t err_code;
uint8_t firstByte = module + command;
if (command > 31 || module > 7) {
return APP_ERROR_INVALID_CMD;
}

uint8_t msg_a[] = {firstByte, mmc_command, mmc_arg >> 24, mmc_arg >> 16,  mmc_arg >> 8, mmc_arg & 0xFF};
uint8_t msg_length = sizeof(msg_a);

memset(m_rx_buf_ext, 0, msg_length);

spi_xfer_done = false;

err_code = nrf_drv_spi_transfer(&spi, msg_a, msg_length, m_rx_buf_ext, msg_length);
while (!spi_xfer_done) {
__WFE();
}
if (err_code != NRF_SUCCESS) {
NRF_LOG_PRINTF("Error during transfer : %d\n", err_code);
}
return err_code;
}

ret_code_t initMMC() {
ret_code_t err_code;
bool mmc_initialized = false;

nrf_delay_us(200);  /**< Wait for more than 74 clock cycles before issuing a command. */
err_code = sendCommand(MODULE_CMD_SEND, CMD_SEND_CMD_TO_MMC, CMD0, 0);

while (!mmc_initialized) {
nrf_delay_us(300);
err_code = sendCommand(MODULE_CMD_SEND, CMD_SEND_CMD_TO_MMC, CMD1, ARG_BUS_INIT);

nrf_delay_us(600);

memset(m_rx_buf, 0, 4);
spi_xfer_done = false;

APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, m_tx_buf, 4, m_rx_buf, 4));

while (!spi_xfer_done)
{
__WFE();
}

if (m_rx_buf[0] == 0xC0 || m_rx_buf[0] == 0x80 || m_rx_buf[0] == 0xE0) {
NRF_LOG_PRINTF("eMMC Initialized.\n");
mmc_initialized = true;
}
nrf_delay_ms(1000);
}
return err_code;
}

void init_clock() {
NRF_CLOCK->LFCLKSRC            = (CLOCK_LFCLKSRC_SRC_Xtal << CLOCK_LFCLKSRC_SRC_Pos);
NRF_CLOCK->EVENTS_LFCLKSTARTED = 0;
while (NRF_CLOCK->EVENTS_LFCLKSTARTED == 0); // Wait for clock to start
}

int main(void) {
APP_ERROR_CHECK(NRF_LOG_INIT());
init_clock();
APP_TIMER_INIT(APP_TIMER_PRESCALER,APP_TIMER_OP_QUEUE_SIZE,NULL);
APP_ERROR_CHECK(nrf_drv_gpiote_init());
buttons_init();
NRF_LOG_PRINTF(NRF_LOG_COLOR_RED"\nSTARTING.\r\n"NRF_LOG_COLOR_DEFAULT);

nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG(SPI_INSTANCE);
spi_config.ss_pin = SPI_CS_PIN;
spi_config.frequency = NRF_SPI_FREQ_4M;
spi_config.mode = NRF_DRV_SPI_MODE_0;
APP_ERROR_CHECK(nrf_drv_spi_init(&spi, &spi_config, spi_event_handler));

resetFPGA(RST_PIN);         //Pulls up fpga's reset pin for 5ms then down
nrf_delay_ms(1000);

APP_ERROR_CHECK(initMMC());
while(1) {
__WFE();
}
}


Do you have an idea on what I'm doing wrong? Thank you very much!

• Are you perfectly sure the clocks are synchronized? – Mast Jul 8 '16 at 10:50
• Which clocks do you mean? The MCU's clock and the SPI clock, generated by the MCU are perfectly in sync yes – Fluffy Jul 8 '16 at 11:25
• How about the SPI and the FPGA? – Mast Jul 8 '16 at 11:34
• What speeds are you using anyway? – Mast Jul 8 '16 at 11:37
• The FPGA's clock is 12MHz, SPI is 4MHz. eMMC for initialization is clocked down to 400kHz (but that's irrelevant) They're not really synchronous since I need one FPGA's clock cycle to perform edge detection on the spi clock – Fluffy Jul 8 '16 at 11:38

The difference between your acquisition speed and data speed is not enough. Increasing the speed of the FPGA or decreasing the speed of the SPI does fix the alignment problem.

Your SPI clock and FPGA clock are not synchronized. This means your FPGA clock needs to run at minimum twice as fast as the SPI clock. However, since SPI is an external signal, it may not be entirely clean. There's likely jitter on your clock and/or data. A factor of 2 as difference is simply cutting it too close.

You have a factor of 3. When in doubt about the validity of your signals, keep in mind that this is close to the bare minimum. Increase the factor. As you indicated, factor 12 works. Your threshold is likely at 8 (12 / 8 = 1.5 MHz).

Also, you most probably don't need such a high speed. Data integrity is likely much more important. Build it safe first, worry about speed optimization later.

Your logic analyzer plot does not have the necessary resolution. But the MISO and MOSI appear to be out of phase by half a cycle.

There are normally 4 modes which SPI buses operate in. In one the data is expected to change on the falling edge and sampled on the rising edge of the clock. In another the opposite is true. If the master and slave were not set the same, the result is unpredictable. However, a bit shift of 1 is a reasonable outcome from such a mismatch.

• Thank you for your answer, I explored that too actually. But the slave and the master are both configured to operate in 0,0 mode. The 'out of phase' issue was symptomatic of the mistake highlighted in the above answer, I think. It is no longer happening – Fluffy Jul 8 '16 at 12:06