Tuesday, 24 December 2019

The boot process of the ZYNQ

Foreword

In the previous post we have scaled up the video frames produced by the VIC-II module so that it can fill the whole screen.

At this point our C64 module has enough functionality implemented so that we can play a game on it. However, everytime we want to play a game with it, the Zybo board need to be attached to a PC, and we need to issue a couple of commands within the Xilinx SDK.

Wouldn't it be nice if the Zybo could just start up on its own and you don't need to connect it to a PC?

This will be the purpose of the next series of posts: To to be able to boot our C64 system on the Zybo board without any hand holding.

To approach this goal, we will work towards been able to run Linux on the Zybo Board. If we are within the Linux ecosystem on the Zybo board, we just have access to so many device drivers, making it easy for us, for instance, to load a tape image from a Micro SDCard.

In this post we will start by getting an overview on how the boot process works on the Zybo Board.

Also, in this in this post I will assume the Zybo board will be connected to a PC running Ubuntu OS or similar.

The Boot Process

When the Zynq is powered up and ready to start the process of booting into an OS, it is faced with a challenging initial condition: The DRAM is disabled.

To get past this hurdle, the Zynq contains onchip memory (OCM). The OCM contains 128KB BootROM code and 256KB SRAM.

The BootROM contains just enough code to load a boot image from a hand full of devices, like a SDCard and QSPI, into SRAM and start execution of it.

256KB of SRAM for loading a boot image is not a lot of memory, so on Zynq devices, the boot process is split into a number of stages:

  • Stage 0: The BootROM starts executing and loads the First stage loader into SRAM
  • Stage 1: The First stage bootloader starts to execute from SRAM. It is responsible for enabling DRAM. Amongst other things it also initialises a number of crucial on chip peripherals as well as a number of clocks on the Zynq. Also the bitstream is read into memory as well as the user application
  • Stage 2: At this stage the DRAM is fully operational and control is handed to the user application. In a Linux ecosystem, we will start with running UBoot, which will eventually load and start a Linux Image.
This is quite a cumbersome process. However, I can understand why this process is necessary. To initialise DRAM is quite a complicated process and one can easily introduce a bug when writing the software routine for this process. Should such a bug exist in BootROM, this could render the Zynq chip useless. Moving DRAM initialisation to software that one retrieves from a SDCard greatly reduces this risk.

Of course SRAM is quite an expensive resource. For this reason the Zynq only contains 256KB of it, and we need three stages of booting instead of just 2.

In this post we will focus on the first stage bootloader (FSBL) and in future posts the Stage 2 bootloader.

Creating a First Stage Bootloader

Let us start by having an overview of the process for creating an FSBL. There is a very nice block diagram of the process here:



The Xilinx SDK get a Hardware handoff file from Vivado and generate the FSBL. On the diagram the FSBL gets aggregated with other files like U-boot and uImage to produce a file called BOOT.BIN.

In this post, however, I will show a method where we will directly invoke the FSBL without having to generate a BOOT.BIN first.

Now, in Vivado let us create a Block Design containing only a Zynq block. Also, let us perform all the suggested wiring:


This block diagram looks very simplistic. However, when you open this block, you will see that it contains very important settings, like DDR settings:

It is these type of settings that Vivado will handoff to the Xilinx SDK and will be incorporated into the FSBL.

With this block design created, do a Synthesise, generate bitstream and an export.

Then, from Vivado launch the Xilinx SDK.

In the File menu, select new application project and provide a name for the application project.

Click next and in the left panel select Zynq FSBL:


When you click finish, a new project will be created and visible in the project panel.

A quick way to test our FSBL, would be to write a message to the console when it runs.

To do this, expand the src folder and open the file main.c. Scroll down to the main method. Just after the call to RegisterHandlers, add the following line:

 xil_printf("Hello World");

Click the down arrow next to the hammer icon and select the Release configuration. Now build the project.

Now, within the sdk folder, within your project folder and under the folder Release, there should be a *.elf file. This is the First stage Bootloader that we will use in the next section.

Testing the First Stage Bootloader

Let us test the FSBL we have created in the previous section.

Ensure the Zybo board is configured to boot into JTAG mode. Hook it up to a PC and power it up.

For his exercise we will need two terminal windows running on the Ubuntu PC. On the first terminal, issue the following command:

sudo screen /dev/ttyUSB1 115200

This is the terminal session where we expect our Hello World message to appear when our FSBL run. The device file might be different in your case or might even be /dev/ttyUSB2.

In the other terminal change to the bin folder of your Xilinx SDK installation and issue the following command:

./xsdb

You will be presented with the XSDB console. At this console, issue the following command:

connect

If you now issue the targets command, you will see a list of targets you can connect to:

xsdb% targets                                                                   
  1  APU
     2  ARM Cortex-A9 MPCore #0 (Running)
     3  ARM Cortex-A9 MPCore #1 (Running)
  4  xc7z010


Our goal is to run our FSBL on the first core, so let us select this core and stop it:

xsdb% target 2                                                                  
xsdb% stop                                                                      
Info: ARM Cortex-A9 MPCore #0 (target 2) Stopped at 0xffffff28 (Suspended)
xsdb% targets                                                                   
  1  APU
     2* ARM Cortex-A9 MPCore #0 (Suspended)
     3  ARM Cortex-A9 MPCore #1 (Running)
  4  xc7z010


We will now load the FSBL into the Zybo board from the location where it was generated. In my case the console output will look as follows:

xsdb% dow ~/fsbl-test/fsbl-test.sdk/fsbl_test/Release/fsbl_test.elf             
Downloading Program -- /home/johan/fsbl-test/fsbl-test.sdk/fsbl_test/Release/fsbl_test.elf
 section, .text: 0x00000000 - 0x0000d38b
 section, .handoff: 0x0000d38c - 0x0000d3d7
 section, .init: 0x0000d3d8 - 0x0000d3ef
 section, .fini: 0x0000d3f0 - 0x0000d407
 section, .rodata: 0x0000d408 - 0x0000d75f
 section, .data: 0x0000d760 - 0x0001054f
 section, .eh_frame: 0x00010550 - 0x00010553
 section, .mmu_tbl: 0x00014000 - 0x00017fff
 section, .init_array: 0x00018000 - 0x00018003
 section, .fini_array: 0x00018004 - 0x00018007
 section, .rsa_ac: 0x00018008 - 0x0001903f
 section, .bss: 0x00019040 - 0x0001ae6f
 section, .heap: 0x0001ae70 - 0x0001ce6f
 section, .stack: 0xffff0000 - 0xffffd3ff
100%    0MB   0.5MB/s  00:00                                                    
Setting PC to Program Start Address 0x00000000
Successfully downloaded /home/johan/fsbl-test/fsbl-test.sdk/fsbl_test/Release/fsbl_test.elf


The FSBL will be loaded at location starting at address 0, which is the area where SRAM resides of OCM.

We can now resume execution of core #0 with the command con. We can now see the output on the other Terminal Window:


Our first stage bootloader worked!

In Summary

In this post we have created and tested a First Stage Bootloader.

In the next post we will continue our journey in getting Linux to run on the Zybo Board.

In particular, we will be getting UBoot to run on the Zybo Board. UBoot is a component you will find in many ARM based systems that assist in booting Linux.

Till next time!

No comments:

Post a Comment