SPI on MicroBlaze not running under FreeRTOS

Hello forum,

I’m using a Xilinx MicroBlaze soft core processor on a Xilinx Kintex UltraScale +.
With bare metal, I was able to communicate with an external SPI device without any problems.

But the same code didn’t work on FreeRTOS. Using an oscilloscope, I found out that there are no signals on the SPI at all.

At the same time, the driver did not report any errors. All parameters used (port ID, chip select line, etc.) are OK.

On the other hand, communiation with my I²C devices works without any problems (I²C is also in polled mode).

The driver written is based on the Xilinx SPI example xspi_polled_example. (So ​​no interrupts are used).
I am using Xilinx Vivado 2018.3.
The FreeRTOS is based on the “FreeRTOS Hello World” example freertos10_xilinx, which is delivered with vivado 2018.3.

Any help is welcome!

regards

Martin F.

You mean POLLED mode, right ?
You should add a few more details and perhaps some code.
I guess you’re using a task per interface running the driver code.
Do they have the same priority ? Otherwise only the highest prio task runs.
Also is the driver aware somehow that it gets preempted by another task for a while ?

Hi Hartmut,

thank you for your response.

yes, i meant ‘polled’. (I’ve fixed that typo).

Due to the bug I’ve reduced my code to the very basic, so that at the moment there are no tasks running at all since all SPI devices should be initialized beforehand outside the scheduler tasks.

So at the moment there is just the initialzation of the XSPI and a read-access to a SPI device.

Here are the snippets of code, but as mentioned, the code works correctly on bare metal:

...
/*
 * Initialize the SPI driver so that it is  ready to use.
 */
ConfigPtr = XSpi_LookupConfig(spi_param->device_id);
if (ConfigPtr == NULL) {
   xil_printf("SPI device not found!\r\n");
   return XST_DEVICE_NOT_FOUND;
}

status = XSpi_CfgInitialize(spi_param->spiInstancePtr, ConfigPtr,
      ConfigPtr->BaseAddress);
if (status != XST_SUCCESS) {
   xil_printf("SPI init interface failed!\r\n");
   return XST_FAILURE;
}

/*
 * Perform a self-test to ensure that the hardware was built correctly.
 */
status = XSpi_SelfTest(spi_param->spiInstancePtr);
if (status != XST_SUCCESS) {
   xil_printf("SPI self-test failed!\r\n");
   return XST_FAILURE;
}

/*
 * Set the SPI device as a master and in manual slave select mode such
 * that the slave select signal does not toggle for every byte of a
 * transfer, this must be done before the slave select is set
 */
status = XSpi_SetOptions(spi_param->spiInstancePtr, XSP_MASTER_OPTION |
      XSP_MANUAL_SSELECT_OPTION);
if (status != XST_SUCCESS) {
   xil_printf("SPI set options failed!\r\n");
   return XST_FAILURE;
}

/*
 * Start the SPI driver so that the device is enabled.
 */
status = XSpi_Start(spi_param->spiInstancePtr);
if (status != XST_SUCCESS) {
   xil_printf("SPI start failed!\r\n");
   return XST_FAILURE;
}

/*
 * Disable Global interrupt to use polled mode operation
 */
XSpi_IntrGlobalDisable(spi_param->spiInstancePtr);

/* DEBUG: Test loop for SPI measurements */

uint8_t scratchpad;
   /* Test read-write-access to the device */
   ad9144_spi_write(dev, REG_SPI_SCRATCHPAD, 0xAD);
   ad9144_spi_read(dev, REG_SPI_SCRATCHPAD, &scratchpad);
   if(scratchpad != 0xAD) {
   xil_printf("%s : scratchpad read-write failed (0x%x)!\n", __func__,
	       scratchpad);
      return XST_FAILURE;
 }
 /* DEBUB-END */


SPI Read and Write functions:

/***************************************************************************//**
 * @brief ad9144_spi_read
 ******************************************************************************/
int32_t ad9144_spi_read(struct ad9144_dev *dev,
			uint16_t reg_addr,
			uint8_t *reg_data)
{
	uint8_t buf[3];
	int32_t ret;

	buf[0] = 0x80 | (reg_addr >> 8);
	buf[1] = reg_addr & 0xFF;
	buf[2] = 0x00;

    XSpi_SetSlaveSelect( dev->spi_dev->spiInstancePtr,
    		dev->spi_dev->slave_select_mask );
    ret = spi_transceive(dev->spi_dev->spiInstancePtr, buf, 2, &buf[2], 1);
    XSpi_SetSlaveSelect(dev->spi_dev->spiInstancePtr, 0x00 );
	if (ret < 0)
		return ret;

	*reg_data = buf[2];

	return ret;
}

/***************************************************************************//**
 * @brief ad9144_spi_write
 ******************************************************************************/
int32_t ad9144_spi_write(struct ad9144_dev *dev,
			 uint16_t reg_addr,
			 uint8_t reg_data)
{
	uint8_t buf[3];

	int32_t ret;

	buf[0] = reg_addr >> 8;
	buf[1] = reg_addr & 0xFF;
	buf[2] = reg_data;

    XSpi_SetSlaveSelect( dev->spi_dev->spiInstancePtr,
    		dev->spi_dev->slave_select_mask );
    ret = spi_write( dev->spi_dev->spiInstancePtr, buf, ARRAY_SIZE(buf) );
    XSpi_SetSlaveSelect(dev->spi_dev->spiInstancePtr, 0x00 );

	return ret;
}

Strange… since it’s still a bare metal driver (no FreeRTOS API used) can you put it in main before anything else / the scheduler is started ? Just to be sure. Then it should work the exactly the same way as using a bare metal application.
Otherwise it might be toolchain/CPU config/FPGA issue.
I guess the bitfile/(E)MIO mapping is the same.

I set up a new project in the SDK with the same bit file but minimized software and a different SPI device (different SPI port).
There are still no signals on the SPI, but this time the software gets stuck on XSPI_transfer ().

I have to discuss this with colleagues. Also if the FPGA designer could add some probes so that I can check what the output of the XSPI core IP is.

If there is any news I will share it here.

Best regards

Martin

Hello Hartmut,

I found the problem. It was a very stupid one.
I used the wrong port IDs from xparamters.h :dizzy_face:

For some reason the IDs within xparameters.h have changed.
And since I used the canonical definitions for the ID ports initialization (e.g. XPAR_SPI_1_DEVICE_ID instead of XPAR_IO_AXI_AD9144_DEVICE_ID) I didn’t notice that.

Thanks for your help.

Best regards

Martin