ZYNQ FSBL – The Saga Continues

Building an FSBL for the ZC706 using Petalinux

Well, another blog post on how to build a modified FSBL for ZYNQ. Using the patch which I demonstrated how to make in the previous post and a modified version of the fsbl_%.bbappend file which I received from the Xilinx Forum post regarding this I was able to make a working FSBL with my patch. The modified fsbl_%.bbappend file is shown below.

# Force to use embeddedsw repository
EXTERNALXSCTsrc=""
EXTERNALXSCTSRC_BUILD = ""

#Enable FSBL debug flags
YAML_COMPILER_FLAGS_append = " -DFSBL_DEBUG"

# Patch FSBL
SRC_URI_append += "file://0001-fsbl.patch"
FILESEXTRAPATHS_prepend := "${THISDIR}/files:"

Here are the steps used to create the FSBL

First run petalinux-create to build a petalinux project. Point it at the appropriate BSP.

$ petalinux-create --type project --source /tools/xilinx/bsp/xilinx-zc706-v2017.4-final.bsp

Then copy the fsbl_%.bbappend file into the project.

$ cp fsbl_%.bbappend xilinx-zc706-2017.4/project-spec/meta-user/recipes-bsp/fsbl
$ cp 0001-fsbl.patch xilinx-zc706-2017.4/project-spec/meta-user/recipes-bsp/fsbl/files

Next run a petalinux-build to make the bootloader

$ petalinux-build --project xilinx-zc706-2017.4 -c bootloader

When all is said and done you will get a zynq_fsbl file in xilinx-zc706-2017.4/images/linux/zynq_fsbl.elf.

This procedure works for 2017.4 and 2016.4.

Building from the git checkout

Alternatively you can build from the git checkout where you made the patch. This seems much simpler but it doesn’t work for me with petalinux 2016.4.

From the embeddesw checkout directory cd to lib/sw_apps/zynq_fsbl_src and run the following command.

$ make BOARD=zc706

This should build the file fsbl.elf in the src directory.

When I try to build this for 2016.4 I get a bunch of errors. They differ depending on how I set the CC variable on the make command line. If I don’t set it I get errors about arm-xilinx-eabi-gcc: Command not found. If I set CC=arm-none-eabi-gcc then I get this

arm-none-eabi-gcc    -c pcap.c -o pcap.o -I../misc//ps7_cortexa9_0/include -I.
In file included from pcap.c:96:0:
pcap.h:65:21: fatal error: xdevcfg.h: No such file or directory
compilation terminated.
Makefile:97: recipe for target 'pcap.o' failed

If anyone knows what to do about this I’m all ears.

Updating the First Stage Bootloader in Petalinux v2017.4

Overview

I am developing a prototype system that uses a lot (9) ZC706 boards. I need each board to boot with a different Ethernet MAC and IP address. I wanted this to be configured using the four DIP switches on the board.

Approach

To do this you need to modify the first stage bootloader (FSBL) to read the dip switch values and then pass the result to U-Boot. There is no built-in mechanism to do this so you need to modify U-Boot as well.

The plan is to modify the fsbl_hooks.c file in the FSBL to read the DIP switch value. Then it will place a message in the on-chip RAM at address 0xfffffc00. This location is unused by U-Boot. We then get U-Boot to look at the message to set the Ethernet address. Simple? Not hardly.

Modify fsbl_hooks.c

I’m not going to go over the FPGA logic for this change. It’s really pretty simple. Hook up the dip switch inputs to a GPIO unit in the PL. See my tutorials if you don’t already know how to do this. Next we modify the FsblHookBeforeHandoff function. First we need to read the DIP switch value. My GPIO block is at address 0x41200000 and I wired the switches into bits 8-11. So we first set those bits to inputs by writing 0xf00 to address 0x41200004 and then we read the dip switch values from address 0x41200000. We need to shift the result right by 8 to align the switch input values.

Next we create a string for U-Boot to consume. This is in the form of a command setting the U-Boot ethaddr variable to the desired MAC address.

u32 FsblHookBeforeHandoff(void)
{
	u32 Status;
	u32 dip_sw;
	int i;
	char ethaddr[40];

	Status = XST_SUCCESS;

	/*
	 * User logic to be added here.
	 * Errors to be stored in the status variable and returned
	 */
	Xil_Out32(0x41200004,0xf00); /* Set DIP switch as inputs */
	dip_sw = Xil_In32(0x41200000)>>8; /* Read values */

	fsbl_printf(DEBUG_INFO,"In FsblHookBeforeHandoff function \r\n");
	sprintf(ethaddr,"ethaddr=%02x:%02x:%02x:%02x:%02x:%02x\n",0x00,0x0a,0x35,0x00,0x00,dip_sw);
	for (i=0; i<strlen(ethaddr)+1; i++)
	  Xil_Out8(0xfffffc00+i,ethaddr[i]);

	return (Status);
}

Modify U-Boot

Next we need to get U-Boot to pay attention to the message. To do this we need to add the following to the project-name/project-spec/meta-user/recipes-bsp/u-boot/files/platform-top.h file.

#undef CONFIG_PREBOOT
#define CONFIG_PREBOOT	"echo U-BOOT for ${hostname};setenv preboot; echo;env import -t 0xFFFFFC00"

The trick is the env import -t 0xFFFFFCOO command. This tells U-Boot to import the string that the FSBL conveniently placed at that address.

That’s not so hard…

Great. That’s all we need to do right? Child, you’ve got a thing or two to learn. So far things have been easy. But even though petalinux-create creates the file components/plnx_workspace/fsbl/fsbl/src/fsbl_hooks.c for you to modify, it actually ignores that file completely. Likewise you could make a copy of the ZC706 BSP file and change the copy in there and then use your modified BSP file. Hah, amateur. That file is also ignored. You can modify the file in the Petalinux install directory but then all your Petalinux builds will get this little change. Clearly not desirable

What we need here is a patch file. We need to place a patch file in the components/plnx_workspace/fsbl/fsbl/src directory and then make a bbappend file that Petalinux will use to patch the fsbl_hooks.c file after it obtains the original file from god knows where but before it builds the FSBL.

Making the FSBL patch file

In order to build the patch you first need to check out the Xilinx embedded software git repository.

git clone https://github.com/Xilinx/embeddedsw.git

Now you need to checkout the branch for the version of Petalinux you are interested in. Here I’m using 2017.4.

$ cd embeddedsw/
$ git checkout tags/xilinx-v2017.4

Next create a branch and check it out. It doesn’t matter what you call the branch. Here I call the branch fsbl_mods_2017.4

$ git branch fsbl_mods_2017.4
$ git checkout fsbl_mods_2017.4
Now edit the file and make the changes.
$ emacs lib/sw_apps/zynq_fsbl/src/fsbl_hooks.c

Now we are ready to make the patch file. First add the file to the branch, and then commit the change.

$ git add lib/sw_apps/zynq_fsbl/src/fsbl_hooks.c
$ git commit -m "Patch to set MAC address based on dip switches" -s

Now we can create the patch.

$ git format-patch -1

This will create a lovely, albeit verbosely named, patch file

0001-Patch-to-set-MAC-address-based-on-dip-switches.patch

. How cool is that?

Here are the contents of my patch file.

From 1bc864fc9d064fd57c0721e27ca04e348d594bd9 Mon Sep 17 00:00:00 2001
From: Pete Johnson <pete@beyond-circuits.com>
Date: Mon, 21 May 2018 17:02:50 -0700
Subject: [PATCH] Patch to set MAC address based on dip switches

Signed-off-by: Pete Johnson <pete@beyond-circuits.com>
---
 lib/sw_apps/zynq_fsbl/src/fsbl_hooks.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/lib/sw_apps/zynq_fsbl/src/fsbl_hooks.c b/lib/sw_apps/zynq_fsbl/src/fsbl_hooks.c
index 304a6db..d86522a 100644
--- a/lib/sw_apps/zynq_fsbl/src/fsbl_hooks.c
+++ b/lib/sw_apps/zynq_fsbl/src/fsbl_hooks.c
@@ -130,6 +130,9 @@ u32 FsblHookAfterBitstreamDload(void)
 u32 FsblHookBeforeHandoff(void)
 {
 	u32 Status;
+	u32 dip_sw;
+	int i;
+	char ethaddr[40];
 
 	Status = XST_SUCCESS;
 
@@ -137,7 +140,13 @@ u32 FsblHookBeforeHandoff(void)
 	 * User logic to be added here.
 	 * Errors to be stored in the status variable and returned
 	 */
+	Xil_Out32(0x41200004,0xf00); /* Set DIP switch as inputs */
+	dip_sw = Xil_In32(0x41200000)>>8; /* Read values */
+
 	fsbl_printf(DEBUG_INFO,"In FsblHookBeforeHandoff function \r\n");
+	sprintf(ethaddr,"ethaddr=%02x:%02x:%02x:%02x:%02x:%02x\n",0x00,0x0a,0x35,0x00,0x00,dip_sw);
+	for (i=0; i<strlen(ethaddr)+1; i++)
+	  Xil_Out8(0xfffffc00+i,ethaddr[i]);
 
 	return (Status);
 }
-- 
1.8.3.1

Adding the patch file to Petalinux

Now we have our magic patch file. Next we need to add it to our Petalinux project. First we copy the patch file to the Petalinux project and rename it to something a little more sane.

$ cp 0001-Patch-to-set-MAC-address-based-on-dip-switches.patch petalinux-project/project-spec/meta-user/recipes-bsp/fsbl/files/0001-fsbl.patch

Next we create a file called petalinux-project/project-spec/meta-user/recipes-bsp/fsbl/fsbl_%.bbappend with the following contents

# Patch for FSBL
# Note: do_configure_prepend task section is required only for 2017.1 release
# Refer https://github.com/Xilinx/meta-xilinx-tools/blob/rel-v2017.2/classes/xsctbase.bbclass#L29-L35
 
do_configure() {
    if [ -d "${S}/patches" ]; then
       rm -rf ${S}/patches
    fi
 
    if [ -d "${S}/.pc" ]; then
       rm -rf ${S}/.pc
    fi
}
 
SRC_URI_append = " \
        file://0001-fsbl.patch \
        "
 
FILESEXTRAPATHS_prepend := "${THISDIR}/files:"

EXTERNALXSCTSRC = ""
EXTERNALXSCTSRC_BUILD = ""

This bbappend script is run during the bitbake Petalinux build and it will patch the appropriate fsbl_hooks.c file during the build.

But wait there’s more…

Now of course we still need to modify the platform-top.h file for U-Boot. Hilariously I went down the same route for U-Boot only to find that there is no platform-top.h file in the git repository for U-Boot. However there is one in the project-spec directory created by petalinux-config. Hmmm… fool me once…

But left with no other choice I tried it – and what do you know? The build does pay attention to this file and U-Boot seems to be build with the addition that we made.

Are we done yet?

Not hardly. As of this writing when I do a Petalinux build the version that is built will not boot. But if I compile the FSBL in the tree that I checked out with my changes I can use that FSBL and boot successfully and the MAC address will be set according to the dip switches. If anyone knows what is going on here I would sure appreciate some advice.

Conclusion

When all is said and done I can build a Petalinux which does what I want. But I don’t understand why modifying platform-top.h in the Petalinux directory works but modifying the fsbl_hooks.c does not. And I’d like to know why the FSBL which gets built with my petalinux build does not work. When I do a find while bitbake is making the FSBL I see the appropriately patched fsbl_hooks.c down in the bowels of the bitbake build. And if there are any (and I mean any) differences in the patch file or the bbappend file then I get errors when bitbake runs. And where did the platform-top.h file come from anyway? That doesn’t seem to be iin the U-Boot tree at all.

Sometimes I think this may all be by design in order to keep embedded Linux consultants employed. Let me know if you have any insights.