
Introduction
For some people, the FPGA SoC is something inaccessible to understanding, and this article should correct this misunderstanding. Let us analyze the creation of a program from scratch, from an empty project to a burning LED. To begin with, I will say that the project was carried out on the DE1-SoC debug board, and you can easily adapt it to other boards with Altera plisses if you understand this manual. Let's start!
Creating FPGA Firmware
To create the firmware, we obviously need a project Quartus. But let's create this project not in the standard way (through the project wizard), but through the utility that came with the DE1-SoC board

')
This utility generates a top-level file written in Verilog with ads of selected items. We need CLOCK, HPS (SoC), buttons, LEDs. Pressing Generate we receive the Quartus project. The main advantage of creating a project in this way is that we do not have to assign FPGA pins to Pin Planner, because the utility did it for us, thereby saving a lot of time.

Add the generated file to the project.
The next step is to create a system in QSYS. Just in case, briefly explain the essence of what is happening. Cyclone V SoC is not just an FPGA, inside its structure is a dual-core Cortex-A9 processor with different modules, such as USB ports, Ethernet, SPI, SD / MMC, etc. In simple terms, you can think of it as a microcontroller inside an FPGA. Creating a system in QSYS we associate a HPS (hard processor system) system with elements synthesized in FPGA using ready-made cores (IP cores). QSYS allows you to connect different kernels through Avalon-MM or AMBA AXI buses without unnecessary worries, we don’t need to write code manually, QSYS generates it for us. Go to QSYS and select our HPS system in the IP cores tab. In the system settings, we need only Lightweight H2F Bridge in the FPGA interfaces tab.

In the Peripheral Pins tab, select SD / MMC (we will load the program from the card) and UART. Later we will tell about it in more detail.

In the HPS clocks tab, leave everything by default. In the SDRAM tab, you must fill all fields with skew values ​​and so on. As far as I understand, they depend on the trace lines from the chip to SDRAM. I could not find any document that has this data for the DE1-SoC, so I took it from the finished project for my board (it was SOC-Computer in the examples of Altera University Program). I saved these settings as a perset, so as not to fill them in anymore, you can see it in the right column. Next in the IP cores find the PIO, it will be our LEDs and buttons.

For the button settings, select Input and 4 bit width as 4 buttons in total

For LEDs, respectively, Output and width 10.

We connect the resulting system as shown in the figure. We can change the names of PIO to make it clearer and more beautiful. The LWH2F bridge is the link between the HPS and the FPGA (it also appoints the HPS master), since the PIO are elements running in the FPGA “fabric”. We double-click on the inscription “Double click for export” opposite the external connection, in order to remove the PIO LEDs and buttons from the system. Later you will see these pins in the top-level code of the generated system file. Since access to the PIO, and to all other cores, in the HPS is carried out by addresses, it is necessary to assign them. This can be done automatically by clicking Assign base addresses.
After that, you can generate a system code.


Specify the path and the desired language code. I prefer Verilog. After that, QSYS can be closed. The following message appears in the Quartus window:

Let's do what we are asked.

In the Files window we see our hps_system.qip. Open it and see the top-level system file.

All that we have chosen in the QSYS system is now in this file. Now you just need to insert this module into the original file. This will be the top-level file of our FPGA firmware.

In it, we attribute the initial I / O pins of this file to the HPS system pins. I remind you that you do not need to assign anything to Pin Planner, everything has already been done when the project was created by the utility. But you need to assign pins HPS, it is done as follows:


Once this is done you can compile the firmware. It is important to note that if any mistakes were made (for example, I forgot to put an end to the hps_system declaration near the slk_clk and hps_io_hps_io_ ... you can see from the figure) you need to re-execute the tcl scripts. Even if everything is written correctly, and you run the tcl scripts, but after running the compilation you get an error, it is worth trying again to run the scripts without changing anything in the code. It helped me, I do not know how to explain this feature.

And so, with the firmware finished! Now proceed to the creation of Preloader.
Create Preloader
The process of loading HPS has several stages, try to understand them. It is worth noting that the Cortex-A9 is a processor for applications, the letter "A" in the name means Application, and is primarily designed to work using the OS, for example Linux. Therefore, strictly speaking, the idea of ​​launching Bare-Metal programs may seem strange, but in some cases this is necessary. In addition, of course, there is such an opportunity, but the developer needs to understand the loading process at least at a basic level.
Immediately after switching on, the code located directly on the Flash memory Cortex-A9 called BootRom is executed. You cannot change it or even view its content. It serves for the initial initialization and in the next stage transfers the boot process to the SSBL (Second Stage Boot Loader called Preloader for short). What you need to know to understand the process is that the BootRom code, first of all, selects the Preloader boot source, focusing on external BSEL physical pins. In the DE1-SoC, the pin configuration was initially selected for loading from an SD card, and it is impossible to change this to, for example, QSPI or NAND flash, without soldering an additional switch and a pair of resistors. Therefore, in QSYS we selected the pins of the SD card in the Peripheral Pins tab. There is also the option of loading not from external sources, but from memory created in FPGA, with the code preloaded there.
And so, after the BootRom code has been executed, the Preloader, which is required to configure Clock, SDRAM and other things, starts loading. After the program starts to run.
To create a Preloader, you need SoC EDS, I'm sure you already downloaded it from the Intel FPGA website.

The program runs from the command line. To begin with, let's create a BSP by writing the appropriate bsp-editor command.

In this window, click New HPS BSP.

You must specify the path to {Project directory} / hps_isw_handoff / and click OK, you do not need to change the other parameters.

Select the spl.boot settings in which we indicate the source from where the Preloader will be loaded. In our case, this is an SD card, so choose BOOT_FROM_SDMMC. We will use the option of downloading Preloader and our program in which there are at least 2 partitions on the flash drive - a partition not formatted with id = A2, for Preloader, and a partition with the FAT32 system, for the program. There is another option without dividing the flash drive into sections, the so-called RAW format, but in our opinion, our option is simpler. You can format the flash drive in this way with any program (I used Mini partition tools 9.2). Or you can use the already assembled image in the folder ... \ embedded \ embeddedsw \ socfpga \ prebuilt_images \ sd_card_linux_boot_image.tar.gz and write it to the USB flash drive via Win32DiskImager.

Choose FAT_SUPPORT, FAT_BOOT_PARTITION 1, FAT_LOAD_PAYLOAD_NAME .img. We will not use WATCHDOG_ENABLE and EXE_ON_FPGA (we are not going to load the Preloader with FPGA).

Here is a very subtle point where I got caught and spent a whole month looking for a problem when I first became acquainted with SoC. Serial Support means that Preloader will already use the UART module to display diagnostic messages right at boot time. Semihosting means that when these diagnostic messages are displayed, they will be automatically displayed in the debugger window when debugging. Using this function in the program itself is very convenient, it allows you to display everything that is written in the printf function into the debugger window without writing any additional code. If you do not specify the use of a UART in the QSYS settings in HPS, but tick the Serial Support checkbox in BSP, this Preloader will not work. If you remove Serial Support, while leaving Semihosting, nothing will work either, at least for me. So you can experiment or just leave a tick, if you want everything to work. Click Generate, then Exit.

Now we change the working folder of the SoC EDS with the command cd "<specify the full path to the generated BSP files (by default this is ... software / spl-bsp)>" To build the Preloader, execute the make command. Expect. At the end of the process in the pack, we get the desired preloader-mkpimage.bin file.
This is a compiled file containing the same 4 Preloader images. Using the mkpimage command executed in the SoC EDS, you can parse this file into its components (separate images), and then assemble it from other configurations (with different Preloader images). Other configurations can be completely different (different systems in QSYS), that is, we get separate preloader-mkpimage.bin files with 4 identical images (64kb each), disassemble them into components and now you can build a new preloader-mkpimage.bin with different images (for example, with different download sources). This is done for reliability. Suppose it so happened that some force did not allow to boot from the first attempt, from the first image. Then the loading of the second image begins, and he, for example, is slightly different for an emergency. If this failed to load, then go to the third and so on. But this is no longer the subject of our task, rather a lyrical digression from the topic, so we continue!
Insert a flash drive with an already prepared partition A2 and write our preloader-mkpimage.bin to it with the command "alt-boot-disk-util -p -a write –d". SoC EDS should point to the path to the folder where the Preloader file is located. Everything is almost ready to write the program! Just create header files with defaults QSYS elements for convenience. Change the path of the SoC EDS, pointing to the firmware file, and execute the command sopc-create-header-files .sopcinfo. At the output we get several files, looking at the contents of which it becomes clear why they are.

Program
For writing and debugging the program, the manufacturer recommends using the DS-5 Eclipse environment. It is recommended to run Eclipse through SoC EDS with the “eclipse &” command (the "&" sign at the end of the command is placed so that the EDS SoC window is active after opening Eclipse).
For those who are already familiar with ARM and have ever written a program for such an architecture, the difficulties end. For those unfamiliar with ARM, the difficulty continues.
Create an empty "C" project. There will be a compiler selection window, here everyone is free to choose and deal with what suits him best. I, as a newcomer to ARM, liked Arm Compiler 5 more, mainly because of the relatively simple synctic scatter files used to place the written program in different parts of the program (the GCC linker script syntax only scares me). In the settings of the project, everything looks trivial. I will point only to this command.

Do the same. This converts the axf format to the bin format. We will need this later to write the program to the USB flash drive. We finally write it.

Here even stdio.h is not required. This program simply assigns the contents in memory at KEYS_BASE to the address LEDS_BASE. By pressing a button on the board, the LED will turn off that hour.

The contents of the scatter file.
To debug debugger you need to write a script. As you remember the boot process is not easy. The script stops Preloader execution at the stage of downloading the application from the source and transfers this task to the computer.

Do not forget to flash the board before downloading the program and debug it!

In the debug window, you need to create a new task, this is done in the Debug control. I forgot to take screenshots at this stage, so I borrowed them. Apply the settings by analogy with those in the pictures below.



Now, after compiling, you can debug the program, view the contents of the registers and do a lot of interesting things.

Once you have verified that the program is working correctly, you can create an image of the program for self-loading. To do this, from the Debug folder with the DS-5 project, take the <prj_name> .bin file and convert it into the .img format using SoC EDS. This is done with the command "mkimage -A arm -O u-boot -T standalone -C none -a 0x00100000 -e 0x00100000 -n" baremetal image "-d .bin .img." Where "-a" is the address to download, and " -e "program entry point.
The entry point can also be set in the project itself, if interrupt vectors are used, they are not used here, therefore we do not specify. I am guided by what addresses I set in the scatter file, and write the same in this command. Before execution, do not forget to specify the path to the bin file in the SoC EDS with the cd command "<folder with file>". The name of the img file should match the one you specified in the BSP editor in FAT_LOAD_PAYLOAD_NAME.
Now just copy the file to the USB flash drive in the fat section, just like a regular file. By inserting a USB flash drive into the DE1-SoC, you can see the execution of the program.
Conclusion
In this article, many points could be considered in more detail, since the described process has many stages, and each has alternative options for implementation and its own characteristics. But I think that the list of references will perfectly answer all other questions for me. Read the following article
Running AMP Applications on Cyclone V SoCBibliography
- Cyclone V Hard Processor System Technical Reference Manual
- Altera SoC Embedded Design Suite User Guide
- HPS SoC Boot Guide - Cyclone V SoC Development Kit
- Bare Metal User Guide
- SoC-FPGA Design Guide