Table of Contents

How To Store Your SDK Project in SPI Flash

Overview

This guide will show you how to easily store your Microblaze/SDK project in the SPI Flash on your 7 series device. Storing a program in Flash allows you to preserve your project through power cycles.


Prerequisites

This guide requires the existence of a Vivado project containing a Microblaze system built complete with Quad SPI, External Memory, and Uart cores, and that you have the appropriate QSPI mode jumper setting on the board. You can follow the steps presented in the Microblaze tutorial, and see below the missing steps for adding QSPI Flash and perform all the connections. Since there are slight differences in the Vivado 2019.1 and the version in which the Microblaze tutorial was created, the user needs to check all the connections in the block design to make sure that are correctly made or present.

Hardware

Software

Board Support Files

Tutorial

Vivado Steps for creating the block design and generate bitstream

1. Create new project

When you first run Vivado this will be the main start window where you can create a new project or open a recent one.

2. Creating New Block Design

This is the main project window where you can create a IP based block design or add RTL based design sources. The flow navigator panel on the left provides multiple options on how to create a hardware design, perform simulation, run synthesis and implementation and generate a bit file. You can also program the board directly from Vivado with the generated bit file for an RTL project using the Hardware Manager. For our design, we will use the IP Integrator to create a new block design.


3. Adding Microblaze IP and Customization

4. Customize Clock Wizard IP Block


5. Adding UART IP Block

6. Add AXI Quad SPI IP

From the Add IP option, select AXI Quad SPI IP with the following customization: Mode - Quad, slave device - Spansion (or Macronix for boards with Macronix flash)

7. Running Connection Automation for the First Time

The clk_out2 pin will be manually connected later.

Do not select Run Connection Automation at this point.

8. Perform some manual connections/check the connections

9. Adding and Customizing Memory Interface Generator IP Block

Connect the clocking wizard “clk_out2” (200 MHz) clock to “sys_clk_i” of the Ram controller. Run connection automation. Right click the DDR controller “DDR2” pin and make it external.

10. Running Connection Automation for the Second Time

11. Regenerate layout and validate design

The result should look like in the below image representing the Block Diagram:

12. Create HDL wrapper

Right click on the Block Design, and select the Create HDL Wrapper option.

13. Synthesize

Open the synthesized design, and under I/O Ports tab below the page, check the name of the signals to be written in the XDC file.

14. Create an XDC file and add the constraints in it

You can copy paste the syntax from the Master XDC file for Nexys4DDR

XDC File signals
## This file is a general .xdc for the Nexys4 DDR Rev. C
## To use it in a project:
## - uncomment the lines corresponding to used pins
## - rename the used ports (in each line, after get_ports) according to the top level signal names in the project

## Clock signal
set_property -dict { PACKAGE_PIN E3    IOSTANDARD LVCMOS33 } [get_ports { sys_clock }]; #IO_L12P_T1_MRCC_35 Sch=clk100mhz
create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports {sys_clock}];
set_property -dict {PACKAGE_PIN C12 IOSTANDARD LVCMOS33} [get_ports {reset}]

#compress bit file
set_property BITSTREAM.GENERAL.COMPRESS TRUE [current_design]

#QSPI signals
set_property -dict {PACKAGE_PIN K17 IOSTANDARD LVCMOS33} [get_ports {qspi_flash_io0_io}]
set_property -dict {PACKAGE_PIN K18 IOSTANDARD LVCMOS33} [get_ports {qspi_flash_io1_io}]
set_property -dict {PACKAGE_PIN L14 IOSTANDARD LVCMOS33} [get_ports {qspi_flash_io2_io}]
set_property -dict {PACKAGE_PIN M14 IOSTANDARD LVCMOS33} [get_ports {qspi_flash_io3_io}]
set_property -dict {PACKAGE_PIN L13 IOSTANDARD LVCMOS33} [get_ports {qspi_flash_ss_io}]

#USB-UART signals
set_property -dict {PACKAGE_PIN D4 IOSTANDARD LVCMOS33} [get_ports {usb_uart_txd}]
set_property -dict {PACKAGE_PIN C4 IOSTANDARD LVCMOS33} [get_ports {usb_uart_rxd}]

#DDR2 Memory signals\\
set_property -dict {PACKAGE_PIN R7 IOSTANDARD SSTL18_II} [get_ports {DDR2_0_dq[0]}]
set_property -dict {PACKAGE_PIN V6 IOSTANDARD SSTL18_II} [get_ports {DDR2_0_dq[1]}]
set_property -dict {PACKAGE_PIN R8 IOSTANDARD SSTL18_II} [get_ports {DDR2_0_dq[2]}]
set_property -dict {PACKAGE_PIN U7 IOSTANDARD SSTL18_II} [get_ports {DDR2_0_dq[3]}]
set_property -dict {PACKAGE_PIN V7 IOSTANDARD SSTL18_II} [get_ports {DDR2_0_dq[4]}]
set_property -dict {PACKAGE_PIN R6 IOSTANDARD SSTL18_II} [get_ports {DDR2_0_dq[5]}]
set_property -dict {PACKAGE_PIN U6 IOSTANDARD SSTL18_II} [get_ports {DDR2_0_dq[6]}]
set_property -dict {PACKAGE_PIN R5 IOSTANDARD SSTL18_II} [get_ports {DDR2_0_dq[7]}]
set_property -dict {PACKAGE_PIN T5 IOSTANDARD SSTL18_II} [get_ports {DDR2_0_dq[8]}]
set_property -dict {PACKAGE_PIN U3 IOSTANDARD SSTL18_II} [get_ports {DDR2_0_dq[9]}]
set_property -dict {PACKAGE_PIN V5 IOSTANDARD SSTL18_II} [get_ports {DDR2_0_dq[10]}]
set_property -dict {PACKAGE_PIN U4 IOSTANDARD SSTL18_II} [get_ports {DDR2_0_dq[11]}]
set_property -dict {PACKAGE_PIN V4 IOSTANDARD SSTL18_II} [get_ports {DDR2_0_dq[12]}]
set_property -dict {PACKAGE_PIN T4 IOSTANDARD SSTL18_II} [get_ports {DDR2_0_dq[13]}]
set_property -dict {PACKAGE_PIN V1 IOSTANDARD SSTL18_II} [get_ports {DDR2_0_dq[14]}]
set_property -dict {PACKAGE_PIN T3 IOSTANDARD SSTL18_II} [get_ports {DDR2_0_dq[15]}]
set_property -dict {PACKAGE_PIN T6 IOSTANDARD SSTL18_II} [get_ports {DDR2_0_dm[0]}]
set_property -dict {PACKAGE_PIN U1 IOSTANDARD SSTL18_II} [get_ports {DDR2_0_dm[1]}]

set_property -dict {PACKAGE_PIN U9 IOSTANDARD DIFF_SSTL18_II} [get_ports {DDR2_0_dqs_p[0]}]
set_property -dict {PACKAGE_PIN V9 IOSTANDARD DIFF_SSTL18_II} [get_ports {DDR2_0_dqs_n[0]}]
set_property -dict {PACKAGE_PIN U2 IOSTANDARD DIFF_SSTL18_II} [get_ports {DDR2_0_dqs_p[1]}]
set_property -dict {PACKAGE_PIN V2 IOSTANDARD DIFF_SSTL18_II} [get_ports {DDR2_0_dqs_n[1]}]

set_property -dict {PACKAGE_PIN N6 IOSTANDARD SSTL18_II} [get_ports {DDR2_0_addr[12]}]
set_property -dict {PACKAGE_PIN K5 IOSTANDARD SSTL18_II} [get_ports {DDR2_0_addr[11]}]
set_property -dict {PACKAGE_PIN R2 IOSTANDARD SSTL18_II} [get_ports {DDR2_0_addr[10]}]
set_property -dict {PACKAGE_PIN N5 IOSTANDARD SSTL18_II} [get_ports {DDR2_0_addr[9]}]
set_property -dict {PACKAGE_PIN L4 IOSTANDARD SSTL18_II} [get_ports {DDR2_0_addr[8]}]
set_property -dict {PACKAGE_PIN N1 IOSTANDARD SSTL18_II} [get_ports {DDR2_0_addr[7]}]
set_property -dict {PACKAGE_PIN M2 IOSTANDARD SSTL18_II} [get_ports {DDR2_0_addr[6]}]
set_property -dict {PACKAGE_PIN P5 IOSTANDARD SSTL18_II} [get_ports {DDR2_0_addr[5]}]
set_property -dict {PACKAGE_PIN L3 IOSTANDARD SSTL18_II} [get_ports {DDR2_0_addr[4]}]
set_property -dict {PACKAGE_PIN T1 IOSTANDARD SSTL18_II} [get_ports {DDR2_0_addr[3]}]
set_property -dict {PACKAGE_PIN M6 IOSTANDARD SSTL18_II} [get_ports {DDR2_0_addr[2]}]
set_property -dict {PACKAGE_PIN P4 IOSTANDARD SSTL18_II} [get_ports {DDR2_0_addr[1]}]
set_property -dict {PACKAGE_PIN M4 IOSTANDARD SSTL18_II} [get_ports {DDR2_0_addr[0]}]

set_property -dict {PACKAGE_PIN R1 IOSTANDARD SSTL18_II} [get_ports {DDR2_0_ba[2]}]
set_property -dict {PACKAGE_PIN P3 IOSTANDARD SSTL18_II} [get_ports {DDR2_0_ba[1]}]
set_property -dict {PACKAGE_PIN P2 IOSTANDARD SSTL18_II} [get_ports {DDR2_0_ba[0]}]

set_property -dict {PACKAGE_PIN L6 IOSTANDARD DIFF_SSTL18_II} [get_ports {DDR2_0_ck_p[0]}]
set_property -dict {PACKAGE_PIN L5 IOSTANDARD DIFF_SSTL18_II} [get_ports {DDR2_0_ck_n[0]}]

set_property -dict {PACKAGE_PIN N4 IOSTANDARD SSTL18_II} [get_ports DDR2_0_ras_n]
set_property -dict {PACKAGE_PIN L1 IOSTANDARD SSTL18_II} [get_ports DDR2_0_cas_n]
set_property -dict {PACKAGE_PIN N2 IOSTANDARD SSTL18_II} [get_ports DDR2_0_we_n]
set_property -dict {PACKAGE_PIN M1 IOSTANDARD SSTL18_II} [get_ports {DDR2_0_cke[0]}]
set_property -dict {PACKAGE_PIN M3 IOSTANDARD SSTL18_II} [get_ports {DDR2_0_odt[0]}]
set_property -dict {PACKAGE_PIN K6 IOSTANDARD SSTL18_II} [get_ports {DDR2_0_cs_n[0]}]

15. Compress Bitstream (Optional)

In certain applications (often when using the Cmod A7 with Microblaze) you might find that you do not have the necessary space in flash in order to store both your program and bitstream configuration. If this is the case it would be worth a try to compress your bitstream in order to get it to fit. Note that using a compressed bitstream in flash will not only take up less space but will also improve initial FPGA programming speeds. These steps take place in Vivado before the bitstream gets generated.

16. Export Hardware Handoff

From File→Export→Export Hardware, make sure to check “Include bitstream” setting:


SDK Steps to create a bootloader and program the board

1. Create SPI Bootloader and BSP

Name the bootloader then hit Next.

2. Configure Bootloader BSP

* Open the BSP project and open system.mss. Click “Modify this BSP's Settings”. The memory device info needs to be passed to the xilifs library (Overview > standalone > xilifs).

Click “Regenerate BSP sources”.

Next we should check that the BSP is updated in order to use the updated version of xilisf. To do this, right click on the BSP and select Board Support Package settings and make sure that xilisf is checked.

It is important to note that this value is the offset that the image will have in Flash.

NOTE: If you are using the Cmod-A7 try the offset: 0x00300000.

NOTE: If you are using the Nexys 4 DDR or Arty-A7-100T try the offset: 0x003D0900.

NOTE: If you are using the Arty-A7-35T, Nexys Video or the Genesys 2 try the offset: 0x00C00000.

NOTE: If you are using a board with Macronix flash follow the additional steps:

#define XISF_MACRONIX_DEV_MX25L3233F 0x2016  /**< Device ID for MX25L3233F */ 
{XISF_MANUFACTURER_ID_MACRONIX, XISF_MACRONIX_DEV_MX25L3233F,
XISF_BYTES256_PER_PAGE, XISF_PAGES256_PER_SECTOR,
XISF_NUM_OF_SECTORS64}

Important if you are using a board with Macronix flash.

The source files that need editing are part of a library (https://xilinx-wiki.atlassian.net/wiki/spaces/A/pages/18841939/xilisf) compiled into a bsp/domain. By navigating to the source files in SDK/Vitis, the local copy of the library's source files are opened. Any modification is done on this local copy and build into the static library (and application) upon build. However, when the source files of the bsp/domain are re-generated (context menu, Regenerate BSP sources), the changes made as explained above are overwritten from the originals in the Xilinx install directory. Take care this does not happen.

A more permanent solution is forking the library repository (https://github.com/Xilinx/embeddedsw) editing the sources there and including a path to the modified repository in SDK/Vitis Xilinx→Repositories→Global.

3. Program FPGA with bootloader

The hardware design including bootloader will be programmed into flash such that the FPGA loads it every time it boots. Configure the SPI jumper on the Nexys4 DDR board on the QSPI position. This allows the FPGA to load its bit file from the SPI flash. The FPGA bit file contains the configuration of the FPGA including initial contents of the Microblaze local BRAM.
Next connect the Nexys4 DDR board via the JTAG usb connector. Start a serial terminal (eg. Teraterm) and select the port and set the baud rate to 9600 (as configured in the UARTlite block design).

Select the bootloader.elf file (in the Bootloader/Debug project) instead of “bootloop”. Selecting “program” will generate a download.bit file in the “design_wrapper_hw_platform_0” project.

This will run update_mem, and will output a download.bit file.

4. Flashing FPGA

The Program Flash Memory utility will be used again to store the bitstream initialized with the bootloader in BRAM. Move the jumper on the board in the JTAG position. The Image File is the download.bit that was generated previously, and should be located inside of your hardware platform folder.The Offset is 0x0 since the bitstream will be loading our project into memory. Select Program to continue. The flash type is “s25fl128sxxxxx0-spi-x1_x2_x4”. Check “verify after flash”. And finish by clicking “Program”.

After flashing you can restart the Nexys4 DDR by pressing the “prog” button. Note that while the FPGA can load its bit file from flash it can also still be programmed as usual using JTAG.

5. Create a User Application

This application will be the one that gets stored into Flash. For this tutorial we will be creating a simple Hello World project but the process applies to any project that you would want stored in flash.

6. Configure User App BSP

Place all sections into DDR:

Select “xilisf” in the navigation tree view. Set the “serial_flash_family” type to 5 (Spansion).

7. Program Flash

In this step we will use the SDK Program Flash Memory utility to program our Hello World application to Flash.

Reset the Nexys4 DDR by pressing the “prog” button. Move the jumper back to QSPI position, start the board again, and run the User application from SDK, check the message displayed in the serial terminal. The bootloader should start loading and eventually run the user application.

Test

The flash has now been programmed with our Hello World, SREC, bitstream, and bootloader. With your board connected to a serial port reset the board. This will configure the FPGA and run our Hello World application!