XModem Bootloader
Introduction
Hardware
Address Map
F2S (ECPS64)
1st Stage Bootloader
2nd Stage Bootloader
XModem upload
XBOOT Header
Blinky example
How to build
How to start
Download
 
Note: XIP (Execute in Place) functionality is no longer available in NEORV32. Therefore, the F2S (Flash to SDRAM) functionality is provided as a replacement.

A project must be created as a F2S project. This is nothing other than a normal SDRAM project, but it is later saved in flash memory. It is then later copied into the SDRAM by the second stage bootloader and started there.

This project use the Quartus Prime Lite software version 20.1.1 and the NEORV32 v1.12.3.3

Introduction

This project here is a further development of an existing XModem Bootloader which was also created for a DE0-Nano board but for a NIOS II CPU.

With this project it should be possible to load a program into one of the different memories and start it there:

  • F2S (Flash to SDRAM, see note above)
  • IMEM (TCM, Tightly Coupled Memory)
  • SDRAM

In addition, it should also be possible to update the FPGA image.

Furthermore, the existing DE0-Nano size configuration with IMEM of 32K, DMEM of 16K and ICACHE of 8K should be retained. Due to this condition, the bootloader could only have a maximum size of 8K.

Because of the size limitation, the bootloader has now been split into two parts:

  • 1st Stage Bootloader in ROM area
  • 2nd Stage Bootloader in F2S area

Note: Compared to the original NEORV32 bootloader, this one uses 115200 baud for output via the terminal.

Hardware

Here a DE0-Nano board with the "JTAG Terasic Adapter" was used:

Address Map

The settings on the NEORV32 result in the following address map:

 Peripheral  Address Offset  Size (bytes)  Attribute
 DM  0xFFFF0000  64K  OCD address space
 SYSINFO  0xFFFE0000  64K  System Information Memory
 GPIO  0xFFFC0000  64K  General Purpose Input / Output
 WDT  0xFFFB0000  64K  Watchdog Timer
 TRNG  0xFFFA0000  64K  True Random Number Generator
 SPI  0xFFF80000  64K  SPI Controller
 UART0  0xFFF50000  64K  Primary UART
 CLINT  0xFFF40000  64K  Core Local Interruptor
 GPTMR  0xFFF10000  64K  General Purpose Timer
 Bootloader ROM  0xFFE00000  64K  Bootloader address space
 F2S  0xE07E0000  128K  F2S 2nd Stage Bootloader
 F2S  0xE0100000  7M - 128K  F2S User Memory
 F2S  0xE0000000  1M  F2S FPGA Memory
 SDRAM  0x91FE0000  128K  2nd Stage Bootloader
 SDRAM  0x90000000  32M - 128K  External SDRAM
 DMEM  0x80000000  16K  On-chip Data Memory
 IMEM  0x00000000  32K  On-chip Instruction Memory
 ICACHE  - - - - - - - - -  8K  On-chip Instruction Cache

F2S (EPCS64)

The DE0-Nano board is equipped with an EPCS64 or EPCS64 compatible serial configuration device. This configuration device is like a SPI flash memory. In case of the XModem Bootloader the address map looks like:

 Area  SPI Address Offset  F2S Address Offset  Size (bytes)
 FPGA image  0x00000000  0xE0000000  1M
 User area  0x00100000  0xE0100000  7M

The EPCS64 has a size of 8 MBytes. Where the first 1 MByte is reserved for the FPGA image with the 1st Stage Bootloader. The user area, starting by 0x00100000, can be free used.

1st Stage Bootloader

The 1st Stage Bootloader only has the task of starting the 2nd Stage Bootloader from the F2S.

Oops, how did the 2nd stage bootloader first get into the F2S? This is explained further down in the "How to start" section.

Normally you do not see the output of the 1st stage Bootloader because the start of the 2nd Stage Bootloader runs very quickly. The start process can be paused by holding down Key0, while powering on, of the DE0-Nano. In addition, the system information of the NEORV32 is also displayed:

The system information is also automatically displayed in the event of an error. Here because the 2nd Stage Bootloader could not be found at address 0xE07E0000.

In addition, LED0 is flashes very quickly (100ms) in case of this error.

2nd Stage Bootloader

The primary task of the 2nd Stage Bootloader is to start the program at address 0xE0100000. If the image is valid, it will be started automatically. But the boot process can be aborted within 3 seconds:

If the boot process is interrupted you have the following options:

With e.g. "i" you can now display information about existing images:

Currently there is a program in the F2S and the 2nd Stage Bootloader is available. No programs are stored in the TCM and SDRAM area.

Note: The bootloader will only start automatically the program from the F2S area. A program from TCM or SDRAM must be started by hand. For this there are the options "t" and "s" available.

There are also the options "E", "F", "X" and "B" for erasing the corresponding areas available.

If there is no program in the F2S area available, then the 2nd Stage Bootloader automatically starts the XModem upload mode:

XModem upload

Here I use TeraTerm which has the possibility to send data via XMODEM. While the data is transferred, the output looks like:

If the transfer is finished, some tests will be done with the image. If the tests was successful, the new image will be stored in the memory. The output for a F2S image upload looks like:

The program can now be started with the option "x".

XBOOT Header

The data received from the Bootloader must contain a valid XBOOT header. The XBOOT header looks like:

The header and data are secured by a checksum. The data follows directly after the XBOOT header. "StartAddress" is the start address of the XBOOT header in the F2S, TCM or SDRAM memory. The size of the update file, header and data, must be 4K bytes aligned. 4K bytes is the size of a sector inside the EPCS memory.

The update file for the application can be generated with the tool "bin2xboot".

The update file for a FPGA image can be generated with the tool "rpd2xboott". This little tool requires an Altera RPD (Raw Programming Data File) file for input and generates the XBOOT (XBO) update file.

Note: A program that is to be stored in the F2S area at 0xE0100000 must be linked starting at 0x90000040. An offset of 0x40 compared to the "StartAddress" is required here, because the XBOOT header itself has a size of 0x40.

If a program is stored in the IMEM at address 0x00000000 must be linked accordingly with 0x00000040. The same applies to the SDRAM, stored at 0x90000000 and linked starting at 0x90000040.

Blinky example

The memory areas were defined in a way that they start with an offset of 0x40:

This means that the XBOOT header can now be placed in front of the actual BIN file without any problems. In the project there are now the different configurations available:

Here the "F2S" is selected. This mean the project should be stored in the F2S area, but will be executed in the SDRAM. However, since the program is for the F2S area, it must be generated for 0xE0100000 and linked for 0x0x90000040.

There is also a "post-build command" that converts the BIN file into an XBOOT (xbo) file. The "StartAddress" for the XBOOT header is also specified here as a parameter.

(Click inside the picture to expand)

If the program was successfully compiled by a "Rebuild", a "build" folder is automatically created in which the file "neorv32-blinky.xbo" should now be located. This XBO file can now be used for an upload with the bootloader and the XMODEM protocol.

You can use the other configuration "SDRAM Release" or "TCM Release" to create XBO files for uploading in the IMEM (TCM) or SDRAM area.

Btw, for the software development SEGGER Embedded Studio was used.

How to build

The project structure looks like this:

Next, I will briefly describe the subprojects and show the configuration with which they have to be compiled for a release version.

bin2xboot is a small utility to write the XBOOT header in front of the BIN file. bin2xboot is a command line tool and was compiled with Microsoft Visual C++ 6.0. Don't worry, you does not need to compile it by yourself. An executable is available in the tools folder inside the projects which need this little tool.

blinky-tcts, I already mentioned blinky above. Blinky is my little test program which uses TinyCTS/AL as operating system.

Configuration:

The "Release" configuration can be used to create XBO files for the relevant areas such as SDRAM, IMEM (TCM) and F2S. With the "Debug" configuration, the program can be debugged in the corresponding area. Oops, with the configuration "TCM Debug" either the IMEM area is too small or the program is too big ;o)

first-boot is the 1st Stage Bootlaoder and resides in the ROM area. The first part of the bootloader is only there to load the second part. Since the actual functionality was too big for the ROM area, or rather you would have lost FPGA "space" as a result, the actual bootloader functionality was moved to the second part.

Configuration:

The "ROM Release" configuration must be used here to create a release version. Then the BIN file can be converted into a VHDL file with "image_gen" later. After you have successfully compiled the project with "Rebuild":

Can you then use "_create_boot_vhd" to create the VHDL file:

This creates the "first_stage_bootloader.vhd" file in the "build" folder. Now copy this file into the "fpga/src" folder:

The next time the FPGA is compiled, the new 1st Stage Bootloader will be included also.

fpga is the project for creating the FPGA image. Here Quartus Prime Lite 20.1.1 was used. There really isn't much else to tell here. But, wait, NEORV32 is used as the CPU here.

second-boot this is the 2nd stage Bootloader. I already described the functionality above.

Configuration:

The "SDRAM2 Release" configuration must be used here to create a release version. After you have successfully compiled the project with "Rebuild", "_create_sdram2_fw" must be used to create the XBO file.

The "SDRAM2 Debug" configuration can be used for testing the 2nd Stage Bootloader with the development environment.

How to start

This current version of the XModem bootloader is not compatible with previous versions. To avoid installation issues, the SPI Flash of the DE0-Nano board should be properly erased.

  • blinky-tcts in "F2S Release" mode => blinky-tcts.xbo
  • first-boot in "ROM Release" mode => first_stage_bootloader.vhd
  • second-boot in "SDRAM2 Release" mode => second-boot.xbo

Next, copy first_stage_bootloader.vhd into the fpga/src directory and start the FPGA build process in Quartus. After that, the SOF file can be loaded into the FPGA using the programmer. This will start the "first-boot" bootloader. You should now see output on the terminal (115200,8,N,1). Since the "second-boot" bootloader is missing, an error message will be displayed. Now generate neorv32-de0n.xbo using the _create_xbo.bat file.

Using SEGGER Embedded Studio, the "second-boot" bootloader can now be compiled in "SDRAM2 Debug" mode and loaded via JTAG into the NEORV32 CPU of the FPGA. After starting, you should see the terminal output as described under "2nd Stage Bootloader". The bootloader should now be in "XModem upload" mode. As described under "XModem upload", you can now upload the second-boot.xbo file. The file will be automatically stored in SPI Flash.

Next, start the "XModem upload" again by pressing "u". Now upload the FPGA image neorv32-de0n.xbo. This file will also be automatically stored in SPI Flash.

The FPGA image along with the "first-boot" and "second-boot" bootloaders are now stored in SPI Flash, and the FPGA can be powered off and on again. After rebooting, the NEORV32 CPU should start automatically and display output from the "second-boot" bootloader on the terminal. Since no image is present yet, an error message will be shown. The "XModem upload" mode will also be automatically activated.

Now upload the file blinky-tcts.xbo. This file will also be automatically stored in SPI Flash. After another power cycle of the FPGA, the Blinky program should now start.

Happy coding...

Download

The repository for this project can be found on GitHub at neorv32-de0n-xboot.