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:
- XIP (Execute in Place memory, existing SPI flash on the DE0-Nano)
- IMEM (TCM, Tightly Coupled Memory)
- SD Card to SDRAM (under development)
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 2K 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 XIP area
Note: Compared to the original NEORV32 bootloader, this one uses 115200 baud for
output via the terminal.
Here a DE0-Nano board with the "JTAG Terasic Adapter"
The settings on the NEORV32 result in the following address map:
|| Address Offset
|| Size (bytes)
|| System Information Memory
|| General Purpose Input / Output
|| Watchdog Timer
|| True Random Number Generator
|| SPI Controller
|| Primary UART
|| Machine System Timer
|| General Purpose Timer
|| Execute In Place Module
| On-Chip Debugger
|| OCD address space
| Bootloader ROM
|| 1st Stage Bootloader
|| 2nd Stage Bootloader
|| 32M - 128K
|| External SDRAM
|| On-chip Data Memory
|| XIP 2nd Stage Bootloader
|| 7M - 128K
|| XIP User Memory
|| XIP FPGA Memory
|| On-chip Instruction Memory
|| - - - - - - - - -
|| On-chip Instruction Cache
The DE0-Nano board is equipped with an
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:
|| SPI Address Offset
|| XIP Address Offset
|| Size (bytes)
| FPGA image
| User area
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 XIP.
Oops, how did the 2nd stage bootloader even get into the XIP for the first time?
For this there is now a FPGA image available with the corresponding reduced bootloader
which can be loaded into the FPGA with the Quartus programmer.
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 0x407E0000.
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
0x40100000. 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 XIP 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 XIP
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 XIP area available, then the 2nd Stage Bootloader
automatically starts the XModem upload mode:
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 XIP
image upload looks like:
The program can now be started with the option "x".
XModem Loader FPGA image
If the 2nd Stage Bootloader is not yet stored in the XIP, it can be stored there with
the development environment, for example. For this you have to be able to debug the
program with JTAG.
A simpler alternative is an FPGA image with integrated XModem Bootloader as SOF file.
The SOF file can now easily be loaded into the FPGA with the Quartus programmer.
Now the 2nd Stage Bootloader can be loaded into the XIP area starting at 0x407E0000.
which simply has to be loaded into the FPGA with the programmer.
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 XIP, TCM or
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)
Note: A program that is to be stored in the XIP area at 0x40100000 must be
linked starting at 0x40100040. 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.
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 "XIP to SDRAM" is selected. This mean the project should be stored
in the XIP area, but will be executed in the SDRAM. The program runs in the XIP at
startup, but then copies itself to SDRAM. However, since the program is for the XIP
area, it must be generated for 0x40100000 and linked for 0x40100040.
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 for RISC-V
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.
bin2vhd is a small utility to convert a binary file to VHDL code. It will be used
for converting the 1st Stage Bootloader to VHDL so that it can then be synthesized
for the FPGA and stored in the ROM area. bin2vhd 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
bin2xboot is also a command line program. It is used to write the XBOOT header in front
of the BIN file. Again, the executable is available in the tools folder in the projects that
need this little tool. It was compiled with Microsoft Visual C++ 6.0 too.
blinky, I already mentioned blinky above. Blinky is my little test program which uses
as operating system. A CoreMark and Dhrystone benchmark are also integrated.
The "Release" configuration can be used to create XBO files for the relevant areas such as
SDRAM, IMEM (TCM) and XIP. 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.
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 bin2vhd 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.
first-xboot is a functionally reduced version of the 2nd Stage Bootloader. But
packaged as a 1st Stage Bootloader. This version was used in the "XModem Loader FPGA image".
The configuration and creation of the final VHDL file use the same steps like in the
fpga is the project for creating the FPGA image. Here Quartus II v15.0.2 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
The "XIP to SDRAM2 Release" configuration must be used here to create a
release version. After you have successfully compiled the project with "Rebuild",
"_create_xip_to_sdram2_fw" must be used to create the XBO file.
The "SDRAM2" configuration can be used for testing the 2nd Stage Bootloader
with the development environment.
xmodem-loader contains only the "XModem Loader FPGA image".
The attentive reader should now know how to create the complete image yourself.
This is now the final exam for you ;o)
The repository for this project can be found on GitHub at