PolarFire® System on a Chip (SoC) Applications - Serial Peripheral Interface (SPI)

Last modified by Microchip on 2026/04/17 16:38

Introduction

The Serial Peripheral Interface (SPI) protocol is a widely used solution that facilitates high-speed, synchronous data transfer. This article focuses on the SPI capabilities of the PolarFire® System on a Chip (SoC) series and will guide you through the basics of using the SPI peripheral on PolarFire SoC boards from:

  • Linux® userspace using the spidev_test utility and writing C application code
  • Bare metal SPI application

Prerequisites

Hardware Setup

  1. This application applies to the following PolarFire SoC-based boards:
  2. Linux host PC for the Yocto Project® building environment or Windows Subsystem for Linux (WSL) on a Windows machine
  3. NAND flash memory Click board™ (MIKROE-Flash-5-Click) for experiments
  4. Digital Logic Analyzer (not required, but useful for observing and analyzing signals)

Software Setup

  1. SoftConsole IDE
  2. Libero® SoC design suite 2024.2+
  3. Yocto Project Host Environment

Additional Resources: SPI Protocol Background

Enabling Microprocessor Subsystem (MSS) SPI in the Libero® SoC Design Suite

To use the Microprocessor Subsystem (MSS) SPI at the application level, the SPI peripheral must be enabled in the Libero SoC design suite. In the reference design that we will use, MSS SPI_1 is enabled in the MSS configurator, and its signals are routed to the mikroBUS™ (MBUS) expansion connector.

Icicle Kit

The reference design is also prepared with future Yocto Project Linux support in mind. When using SPI under Linux, it is essential to know how the SPI interface is connected in the hardware design. The SPI connection mapping for the PolarFire SoC Discovery Kit and Icicle Kit is shown in the following table.

Icicle Kit Discovery Kit
SPI 1Physical ConnectionSPI 0Physical Connection
MOSIMBUS SPI MOSIMOSIMBUS SPI MOSI
MISOMBUS SPI MISOMISOMBUS SPI MISO
SCLKMBUS SPI SCLKSCLKMBUS SPI SCLK
CSMBUS SPI CSCSMBUS SPI CS

We will rely on the PolarFire Icicle Kit reference design created using the Libero® SoC design suite:

Start by downloading the PolarFire Icicle Kit reference design generation FlashPro images from the latest release in the PolarFire SoC GitHub repo:

https://github.com/polarfire-soc/icicle-kit-reference-design/releases

Back to Top


Program the FPGA by the MPFS_ICICLE_KIT_BASE_DESIGN_{VERSION}​​​​ ​​​design using the FlashPro Express tool (included with the Libero SoC design suite).

Open FlashPro Express to program the prebuilt reference programming FPGA fabric logic.

  1. Open FlashPro Express.
  2. Create new project.
  3. Choose the MPFS_ICICLE_KIT_ES_*\MPFS_ICICLE_KIT_ES_BASE_DESIGN_*\MPFS_ICICLE_KIT_ES_*.job file from reference design download folder
  4. Click on Run button.
Warning

You have 2 choices of programming the FPGA:

  1. Use prebuilt reference programming files only (quick start).
  2. Build or modify the Libero SoC design suite project and regenerate programming file using the "PolarFire® SoC Applications - MSS and Libero SoC Design Suite" guide.

Back to Top


Now you have programmed the FPGA fabric logic.

Back to Top

SPI in Linux® Environment

We need to set up and configure the MSS SPI peripheral in the Linux configurations. That's why we need a Linux build system to configure it. For this article, we will use the Yocto Project.

Hart Software Services (HSS) Configurations

HSS (Hart Software Services) is the bootloader for PolarFire SoC. It runs first, sets up hardware, launches Linux or other apps, and is essential for multi-core and secure boot.

Objectives:

  • Download and import HSS to SoftConsole.
  • Update references and build HSS
  • Deploy HSS to PolarFire Icicle Kit
Information

Note: This application article is verified on HSS 2025.07

At first we need to download HSS from GitHub.

Back to Top


Import HSS project to SoftConsole by going to File > Import > Import Existing Project Into Workspace.

Import Projects

Warning

Ensure that you checked Copy projects into workspace.

Browse the HSS folder and import project into workspace by clicking Finish.

Back to Top


Copy your MSS xml file into project.

Copy xml file to hart-software-services/boards/mpfs-icicle-kit/soc_fpga_design/xml/<your xml>.xml.

Information

Note: If your board is production board (not ES): use boards/mpfs-icicle-kit/... instead of boards/mpfs-icicle-kit-es/...

Back to Top


Copy and rename configurations for HSS.

  1. Copy hart-software-services/boards/mpfs-icicle-kit-es/def_config to hart-software-services/.
  2. Rename def_config to .config.
  3. Edit .config file and update path to your xml by changing next line.
CONFIG_SOC_FPGA_DESIGN_XML="boards/mpfs-icicle-kit-es/soc_fpga_design/xml/<your xml>.xml"

Back to Top


Build HSS and deploy.

Right-click on the project name. 

Click on the build project. 

Select PolarFire SoC program non-secure boot mode 1 run option and deploy project to SoC. 

Warning

Make sure are run configurations right. Else open ( External tools -> External tools configurations ), change die and package with your board parameters.
Icicle Kit ES's die is MPFS250T_ES and package is FCVG484

Back to Top

Yocto Project Configurations

In this section, we will create a Linux image and program it into the PolarFire SoC Icicle Kit.

Objectives:

  1. Setting up the Yocto Project building environment.
  2. Enabling the SPI peripheral and including the necessary packages in the build.
  3. Building a Linux image and deploying it into the SoC.

Creating Environment

Open the command line on your Linux machine or the WSL.

Information

Note: If this is your first time building, install the required packages on the Linux machine by executing this command in your command line:

sudo apt-get install gawk wget git-core git-lfs diffstat unzip texinfo gcc-multilibbuild-essential chrpath socat cpio python3 python3-pip python3-pexpect xz-utils debianutils iputils-ping python3-git python3-jinja2 libsdl1.2-dev xterm repo

Back to Top


Create an empty directory to hold the workspace:

mkdir yocto-dev && cd yocto-dev

Back to Top


Use the repo tool to fetch all the required repositories:

repo init -u https://github.com/linux4microchip/meta-mchp-manifest.git -b refs/tags/linux4microchip+fpga-2025.07 -m polarfire-soc/default.xml
repo sync

Back to Top


Set the TEMPLATECONF environment variable to point to the appropriate configuration template before initializing the build environment:

export TEMPLATECONF=${TEMPLATECONF:-../meta-mchp/meta-mchp-polarfire-soc/meta-mchp-polarfire-soc-bsp/conf/templates/default}

Back to Top


Then initialize the Yocto Project build environment:

source openembedded-core/oe-init-build-env

Back to Top


Build the Linux image with default configurations to make sure that all packages and tools are installed and working properly:

MACHINE=mpfs-icicle-kit bitbake mchp-base-image

Back to Top

Configuring SPI Peripheral

Prepare the Microchip Linux kernel (linux-mchp) source tree for local development:

MACHINE=mpfs-icicle-kit devtool modify linux-mchp
Success

We should get the following log:

Recipe linux-mchp now set up to build from /build/workspace/sources/linux-mchp

Back to Top


Add all necessary libraries and apps to include to the build.

Open the conf/local.conf file and add the following variable at the of file:

CORE_IMAGE_EXTRA_INSTALL += "spidev-test packagegroup-core-buildessential vim libudev kernel-modules"

Information
  • packagegroup-core-buildessential - A Yocto Project meta-package that pulls in all the essential build tools needed for compiling software on your embedded system.

    • Includes:

      • gcc → C compiler

      • make → build automation

      • binutils → linker, assembler, etc.

      • pkgconfig → helps locate libraries and headers

      • libc-dev → standard C library headers

      • autoconf, automake, libtool → for building autotools-based projects

  • vim - A powerful text editor used in terminal environments
  • spidev-test - spidev_test sends raw bytes over SPI to test communication with devices like Flash chips. It's great for quick checks via /dev/spidevX.Y.
  • libudev - libudev is a Linux library that lets user-space programs interact with the device manager (udev). It helps detect, enumerate, and monitor hardware devices like USB, SPI, I2C, etc.
  • kernel-modules - kernel-modules are loadable pieces of code that extend the Linux kernel at runtime — without rebooting. They add support for hardware (like SPI, I2C, USB, Flash) or features (like filesystems, networking, etc).
  • mchp-base-image (Release 2025.07) default configuration includes the libgpiod library, you can skip the below steps if you're following the the default configs.

Save the file and exit.

Back to Top


Open the Linux kernel configuration menu to enable the SPI subsystem and SPI controller drivers:

MACHINE=mpfs-icicle-kit bitbake linux-mchp -c menuconfig

Back to Top


Navigate down to the Device Drivers:

Device Drivers

Back to Top


Enable the SPI Support option:

SPI Support

Back to Top


Save the config by selecting Save > Exit.

Back to Top


Locate the PolarFire Icicle kit related Device Tree Source (DTS) files for modification:

yocto-dev/build/workspace/sources/linux-mchp/arch/riscv/boot/dts/microchip/mpfs-icicle-kit.dts
yocto-dev/build/workspace/sources/linux-mchp/arch/riscv/boot/dts/microchip/mpfs-icicle-kit-common.dtsi

Back to Top


Enable the SPI1 and add spidev as a node at the end of the DTS (in .dts file):

1
2
3
4
5
6
7
8
9
10
11
&spi1 {
 status = "okay";

   spidev@0 {
      #address-cells = <1>;
      #size-cells = <1>;
      compatible = "microchip,mpfs-spidev";
      spi-max-frequency = <20000000>;
      reg = <0>;   
   };
};

Back to Top


Compile a customized Yocto Project Linux kernel recipe in a developer-friendly way, producing kernel binaries for PolarFire SoC.

MACHINE=mpfs-icicle-kit devtool build linux-mchp
Warning

You can create a bbappend layer execute following commands
> devtool finish linux-mchp custom-layer

​​​​Your bbappend layer will be saved at: custom-layer/recipes-kernel/linux/linux-mchp_%.bbappend

Adding spidev Driver Support

Locate the spidev.c source code for modification:

yocto-dev/build/workspace/sources/linux-mchp/drivers/spi/spidev.c

Back to Top


Add the compatible string to the spi_device_id and of_device_id structs:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static const struct spi_device_id spidev_spi_ids[] = {
 { .name = "bh2228fv" },
        ..
 { .name = "si3210" },
 { .name = "mpfs-spidev" },
 {},
};
..
static const struct of_device_id spidev_dt_ids[] = {
 { .compatible = "cisco,spi-petra", .data = &spidev_of_check },
        ..
 { .compatible = "silabs,si3210", .data = &spidev_of_check },
 { .compatible = "microchip,mpfs-spidev", .data = &spidev_of_check },
 {},
};

Lines 5 and 13 are added. 

Building Linux Image and Deploying

Execute the command shown below to build the Linux image:

MACHINE=mpfs-icicle-kit bitbake mchp-base-image

Information

Note: Ensure that the initial Linux image has been built without error, so we can start making the modifications.

Here's the list of names of supporting machines. 

MACHINEBoard NameDescription
MACHINE=mpfs-icicle-kitMPFS-ICICLE-KIT-ES, MPFS-ICICLE-KITPolarFire SoC Icicle Kit
MACHINE=mpfs-disco-kitMPFS-DISCO-KITPolarFire SoC Discovery Kit
MACHINE=mpfs-video-kitMPFS250-VIDEO-KITPolarFire SoC Video Kit

Back to Top


As the build is done, you can locate your Linux image at:

yocto-dev/build/tmp-glibc/deploy/images/<board_name>/<image-name>.rootfs-***.wic

Back to Top


Follow the GitHub instructions to deploy the built Image to the eMMC/SD card memory.

Information

For additional information, refer to the "OpenEmbedded/Yocto Project BSP layer for Microchip's SoCs" GitHub page.

Back to Top


After booting Linux on the PolarFire Icicle Board, login as root and check if the SPI device 1 is appeared under the devices:

root@icicle-kit-es:/# ls /dev | grep spidev
spidev1.0

If nothing returns, that means the SPI device is not enabled and you have to double-check the configuration, DTS, and driver modifications needed to be done.

Back to Top

Software

Testing spidev_test Utility

spidev_test is a utility program used to test and verify the functionality of SPI devices on Linux systems. It is typically used to send and receive data over SPI to ensure that the SPI bus and connected devices are working correctly.

The spidev_test program interacts with the SPI device driver through the /dev/spidevX.Y device files, where X and Y represent the bus and chip select numbers, respectively. It allows users to perform basic read and write operations, configure SPI settings such as mode, speed, and word length, and observe the data exchanged between the host (master) (usually the microcontroller or processor) and the SPI target (slave) device.

spidev_test Commands

Here is a list of arguments that can be used with the spidev_test command:

root@icicle-kit-es:~# spidev_test -h
Usage: spidev_test [-2348CDFHILMNORSZbdilopsv]
general device settings:
  -D --device         device to use (default /dev/spidev1.1)
  -s --speed          max speed (Hz)
  -d --delay          delay (usec)
  -l --loop           loopback
spi mode:
  -H --cpha           clock phase
  -O --cpol           clock polarity
  -F --rx-cpha-flip   flip CPHA on Rx only xfer
number of wires for transmission:
  -2 --dual           dual transfer
  -4 --quad           quad transfer
  -8 --octal          octal transfer
  -3 --3wire          SI/SO signals shared
  -Z --3wire-hiz      high impedance turnaround
data:
  -i --input          input data from a file (e.g. "test.bin")
  -o --output         output data to a file (e.g. "results.bin")
  -p                  Send data (e.g. "1234\xde\xad")
  -S --size           transfer size
  -I --iter           iterations
additional parameters:
  -b --bpw            bits per word
  -L --lsb            least significant bit first
  -C --cs-high        chip select active high
  -N --no-cs          no chip select
  -R --ready          slave pulls low to pause
  -M --mosi-idle-low  leave mosi line low when idle
misc:
  -v --verbose        Verbose (show tx buffer)

Using spidev_test on PolarFire Icicle Kit

Information

Note: All the following commands are running from the PolarFire Icicle Kit connected with the Flash 5 Click board on the mikroBUS socket.

Icicle Kit

To verify SPI communication with the Flash 5 Click board, we can use the spidev_test utility to send a test pattern and observe the response. For example, to read the JEDEC ID register (address 0x9F) at a frequency of 1 MHz, execute the following command:

root@icicle-kit-es:~# spidev_test -s 1000000 -D /dev/spidev1.0 -p "\x9F\x00\x00\x00\x00" -v
spi mode: 0x0
bits per word: 8
max speed: 1000000 Hz (1000 kHz)
TX | 9F 00 00 00 00 __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __  |...|
RX | FF FF EF AA 21 __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __  |...|

JEDEC ID register

Information

Note: The command sends five bytes: 0x9F, 0x00, 0x00, 0x00, and 0x00.

  • 0x9F: This tells the board that we want to read from register 0x9F (the JEDEC_ID).
  • First 0x00: This is a dummy byte. We send it just to get board to send us back the actual JEDEC_ID value.
  • Second 0x00: Another dummy byte. Since the board automatically moves to the next register after each read, this lets us grab the value from register 0x9F which contains the manufacturer ID.
  • Third 0x00: One more dummy byte. We're send that byte sends to get the memory type which is right after manufacturer ID.
  • Fourth 0x00: One more dummy byte. We're send that byte sends to get the memory capacity which is right after memory type.
  • And last 0x00. Sends to understand there is nothing to get.

Basically, we send the address, then a couple of dummy bytes to clock out the data we want.

0x9F is the JEDEC ID command. The returned JEDEC ID is typically 3 bytes (manufacturer/memory type/capacity), e.g., EF AA 21 for this device​​​​​​ Flash 5 Click data sheet.

Back to Top

Application Programming in C Language Using spidev With ioct

We can write a C program that runs from userspace and interacts with SPI devices using the ioctl functions. The example below is a C code that performs the same loopback test we did earlier.

Boot Linux on your PolarFire Icicle kit. Navigate to /media and create spi_example.c using the vim editor:

cd /media && vim spi_example.c

Back to Top


Copy and paste the following C code to the spi_example.c:

#include <fcntl.h>
#include
<linux/spi/spidev.h>
#include
<sys/ioctl.h>
#include
<unistd.h>
#include
<stdio.h>
#include
<stdint.h>

int main() {
   int fd = open("/dev/spidev1.0", O_RDWR);
   uint8_t tx[] = {0x9F, 0x00, 0x00, 0x00};
   uint8_t rx[4] = {0};

   struct spi_ioc_transfer tr = {
        .tx_buf = (unsigned long)tx,
        .rx_buf = (unsigned long)rx,
        .len = sizeof(tx),
        .speed_hz = 1000000,
        .bits_per_word = 8,
    };

    ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
    printf("JEDEC ID: %02X %02X %02X\n", rx[1], rx[2], rx[3]);
    close(fd);
   return 0;
}

Back to Top


After saving the modification, compile the C code on target:

gcc spi_example.c -o spi_example

Back to Top


Run the spi_example executable:

./spi_example
Success

Running the application, you will get the following response:

root@icicle-kit-es:/media# ./spi_example
JEDEC ID: EF AA 21

Back to Top

SPI in Bare Metal Applications

To use the MSS SPI in the bare metal side, we have to use the softconsole to develop the application to build, compile, and deploy.

For this application, let us take one of the GitHub bare metal reference examples and change it so it uses the MSS SPI and communicates with the Flash 5 Click board.

Download the mpfs-blank-baremetal baremetal application project from the official GitHub repository and import it into the SoftConsole.

Note: If this is your first time importing a project in SoftConsole, watch this video:

Back to Top


Replace the MSS Configuration XML file in the bare metal project with the XML file used in your Libero SoC design suite project. The path to the file that SoftConsole will use to generate header files, which are then used by the MPFS HAL, is:

mpfs-blank-baremetal > "your_board" > fpga_design > design_description > xx.xml

Back to Top


Now we can begin modifying u54_1.c with our custom code to perform two actions:

Read the ID of the Flash board from register 0x9F referring to the W25N01GVZEIG data sheet.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <stdio.h>
#include
<string.h>
#include
"mpfs_hal/mss_hal.h"
#include
"drivers/mss/mss_mmuart/mss_uart.h"
#include
"drivers/mss/mss_spi/mss_spi.h"


uint8_t tx_buffer[10];
uint8_t rx_buffer[10];

static void mss_spi_overflow_handler(uint8_t mss_spi_core)
{
    (void)mss_config_clk_rst(MSS_PERIPH_SPI1, (uint8_t) MPFS_HAL_FIRST_HART, PERIPHERAL_OFF);
    (void)mss_config_clk_rst(MSS_PERIPH_SPI1, (uint8_t) MPFS_HAL_FIRST_HART, PERIPHERAL_ON);
}


void u54_1(void)
{

    PLIC_init();
    PLIC_SetPriority(SPI1_PLIC, 2u);
    PLIC_EnableIRQ(SPI1_PLIC);
    __enable_irq();


    (void) mss_config_clk_rst(MSS_PERIPH_MMUART1, (uint8_t) MPFS_HAL_FIRST_HART, PERIPHERAL_ON);
    (void) mss_config_clk_rst(MSS_PERIPH_SPI1,    (uint8_t) MPFS_HAL_FIRST_HART, PERIPHERAL_ON);


   // Initialization
   MSS_UART_init(&g_mss_uart1_lo, MSS_UART_115200_BAUD,
        MSS_UART_DATA_8_BITS | MSS_UART_NO_PARITY | MSS_UART_ONE_STOP_BIT);

    MSS_SPI_init(&g_mss_spi1_lo);
    MSS_SPI_configure_master_mode(&g_mss_spi1_lo,
          MSS_SPI_SLAVE_0,
          MSS_SPI_MODE0,
         256u,
          MSS_SPI_BLOCK_TRANSFER_FRAME_SIZE,
          mss_spi_overflow_handler);

    MSS_UART_polled_tx_string(&g_mss_uart1_lo, "Checking connection to Flash\r\n");


    tx_buffer[0] = 0x9FU; // JEDEC_ID register
   MSS_SPI_set_slave_select(&g_mss_spi1_lo, MSS_SPI_SLAVE_0);
    MSS_SPI_transfer_block(&g_mss_spi1_lo, tx_buffer, 1, rx_buffer, 5);
    MSS_SPI_clear_slave_select(&g_mss_spi1_lo, MSS_SPI_SLAVE_0);

   if(rx_buffer[1] == 0xEF){
        MSS_UART_polled_tx_string(&g_mss_uart1_lo, "Connected Successfully\r\n");
    } else {
        MSS_UART_polled_tx_string(&g_mss_uart1_lo, "Connection Failed");
    }
}

Line 1-5: We start by including the necessary header files for standard input/output, string handling, and the PolarFire SoC HAL, UART, and SPI drivers.

Line 8-9: Defining unsigned 8 bit data array for TX and RX data storage.

Line 11-15: Implementing function that reboot SPI peripheral on SPI overflow.

Line 18: The u54_1() function is the main entry point for the application. It waits for a software interrupt to synchronize startup and enables the required peripherals.

Line 27-28: We initialize the interrupt controller and set priorities for SPI-related interrupts. Bringing out of reset UART and SPI peripherals.

Line 32-43: Setting up the SPI and the UART peripherals.

Line 46: Loading JEDEC_ID to the TX buffer.

Line 47-49: Making request to Flash board and getting 5B of response.

Line 51-56: Checking if device manufacturer ID is same as reference manual then prints connection success message else prints connection fail message.

Back to Top


Build the project and deploy it either in LIM for Debug mode or eNVM for Release mode.

Note: You can refer to this building and debugging bare metal applications in SoftConsole video.

Back to Top

Summary

This article explains how to enable and test SPI communication on PolarFire SoC-based boards using Yocto Project. The process begins with configuring the device tree to enable the SPI peripheral, then ensuring the SPI driver is enabled in the Linux kernel. User-space testing is then performed using the spidev utility to interact with SPI devices from the Linux Userspace. Additionally, a custom C program is written and executed to check the SPI Flash board connection. By following this guide, you establish a solid foundation for further development with SPI peripherals on the PolarFire SoC-based boards.

Back to Top