|
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.
|