Hello PetaLinux on the Snickerdoodle Black with piSmasher Baseboard
A walkthrough of bringing up a basic embedded Linux image in PetaLinux 2019.2 for the Snickerdoodle Black with piSmasher baseboard.
When bringing up a new FPGA design, once the hardware is laid out, the engineer has a choice between running a bare metal application in the hard/soft processor in the design. In my last blog on the Snickerdoodle Black with piSmasher baseboard, I covered the process of developing and running a bare metal application on one of the Arm core processors in the Zynq chip. This time, I'll be walking through the process of how to develop and run a custom embedded Linux image on the Arm core processor in the Zynq. This blog picks up at the point where my last blog I mentioned above ends. (Once again, I am running Vivado+Vitis 2019.2 and PetaLinux 2019.2 on Ubuntu 18.04)
Side note: to get the Ethernet ports to actually be pingable, I had to change both of the PHY address values to zero of the GMII to RGMII IP blocks in the Vivado block design as shown below:
The address of chips such as the Marvell PHYs on the Snickerdoodle are normally set by the hardware via bootstraps. In my previous blog post, I had the PHY addresses set to 8 and 9 but when I looked at the Snickerdoodle schematic, I noticed each of the PHYs had their addresses bootstrapped to zero. Since the PHY don't share an MDIO bus with each other or any other device, it is possible for they to both have the PHY address of zero.
Since the CONFIG pin is tied to LED1, the two-bit mapping is set to 10 as defined in Table 55 of the Marvell 88E1510 datasheet, which translates to a PHY address of zero as outlined in Table 56.
With that small fix applied, we can jump back to PetaLinux where first things first, you need to source the PetaLinux settings script:
source <PetaLinux install directory>/settings.sh
With the environment set up, create the PetaLinux project targeting the Zynq platform and give it the desired project name.
petalinux-create --type project --template zynq --name <project name>
The next step is to change directories into the project folder that was just created and import the hardware design into it created in Vivado and also used by Vitis. This is the .xsa file that was exported from Vivado.
petalinux-config --get-hw-description /<path to xsa file>
This command will also will launch the System Configuration ASCII GUI where things such as root file system type, serial port number, Ethernet IP address, and so on is configured for a PetaLinux image. To access the System Configuration GUI after the initial hardware import simply run the following command from inside the PetaLinux project directory:
petalinux-config
Under Subsystem AUTO Hardware Settings, navigate to Advanced bootable images storage Settings and verify that boot image settings, kernel image settings, and dtb image settings are set to primary sd.
The only other tab that needs modifications in the System Configuration is the Image Packaging Configuration tab. Select EXT (SD/eMMC/QSPI/SATA/USB) for the Root file system type and unselect the option to Copy final images to tftpboot.
When you select to exit the GUI, you will be prompted to save the configuration changes and bitbake is run to make an initial check on the configuration. It's at this point that I like to run a build on the project just to get an initial platform down.
petalinux-build
Once the build has completed, there are some kernel drivers that need to be added to the project for the Snickerdoodle+piSmasher design. To access the Kernel Configuration ASCII GUI, run the following command:
petalinux-config -c kernel
Since there are two GMII to RGMII conversion IP blocks in the Vivado hardware block design, the kernel driver for them needs to be included in the PetaLinux build so that the embedded linux image will have the hooks to be able to see them and the user space can ultimately utilize the Ethernet ports on the Zynq chip.
On top of needing the drivers to be able to control the GMII to RGMII IP, the linux image also needs the drivers enabled from Marvell to be able to talk to the two Marvell 88E1510 PHY chips on the piSmasher board for the two Ethernet ports.
As someone that is learning Yocto and how to build an embedded linux image through PetaLinux, I've been finding certain features in the PetaLinux ASCII GUIs very helpful such as the Package Groups options in the root file system configuration editor.
To access the root filesystem configuration editor enter the following command from inside the PetaLinux project directory:
petalinux-config -c rootfs
You have the option to add individual applications and programs, but under the PetaLinux Package Groups tab you have the option to choose groups of relevant applications/programs for a specific purpose. Given that the Snickerdoodle combined with the piSmasher baseboard has both Ethernet and wifi hardware peripherals, I chose to include both the networking-debug and networking-stack groups. I also like to include the python-modules, utils, and x11 package groups as that will give you an overall similar image to Raspbian on a Raspberry Pi.
Once the system has been configured, the device tree needs to have any extra hardware nodes needed added to it. To start off, I'm just adding the two Ethernet ports from the piSmasher to the device tree for now. Incorrect device tree implementations can be difficult to troubleshoot so I prefer to only add one or two hardware peripheral nodes at a time to help keep my troubleshooting focused to a manageable amount of things that can possibly be wrong. PetaLinux will autogenerate the bare minimum needed based on the hardware design exported from Vivado, and a user can add any further hardware devices via the system-conf.dtsi file (located in the <PetaLinux project directory>/project-spec/meta-user/recipes-bsp/device-tree/files directory). This is what I added to the system-conf.dtsi file for the piSmasher Ethernet ports:
/include/ "system-conf.dtsi" / {
aliases{
eth0 = &gem0;
eth1 = &gem1;
};
};
&gem0{
phy-handle = <ð0>;
phy-mode = "rgmii-id";
status = "okay";
gmii2rgmii-phy-handle = <&gmii_to_rgmii_0>;
ps7_ethernet_0_mdio: mdio{
#address-cells = <1>;
#size-cells = <0>;
eth0: phy@0 {
device_type="ethernet-phy";
phy-mode = "rgmii-id";
xlnx,phy-type = <0x5>;
reg = <0>;
};
gmii_to_rgmii_0: gmii_to_rgmii_0@0 {
compatible = "xlnx,gmii-to-rgmii-1.0";
phy-handle = <ð0>;
reg = <0>;
};
};
};
&gem1{
phy-handle = <ð1>;
phy-mode = "rgmii-id";
status = "okay";
gmii2rgmii-phy-handle = <&gmii_to_rgmii_1>;
ps7_ethernet_1_mdio: mdio{
#address-cells = <1>;
#size-cells = <0>;
eth1: phy@0 {
device_type="ethernet-phy";
phy-mode = "rgmii-id";
xlnx,phy-type = <0x5>;
reg = <0>;
};
gmii_to_rgmii_1: gmii_to_rgmii_1@0 {
compatible = "xlnx,gmii-to-rgmii-1.0";
phy-handle = <ð1>;
reg = <1>;
};
};
};
With the device tree updated, the PetaLinux project is ready for its final build:
petalinux-build
Since the Snickerdoodle is being booted from an SD card, the Zynq first stage bootloader elf file, FPGA bitstream, and u-boot elf file need to be packaged into the boot image file, which can be done using the petalinux-package command:
petalinux-package --boot --format BIN --fsbl <vitis workspace>/<project name>/export/<project name>/sw/<project name>/boot/fsbl.elf --fpga <vitis workspace>/<project name>/bitstream/<project name>_wrapper.bit --u-boot <petalinux project directory>/images/linux/u-boot.elf
After the project has successfully built and the boot image has been packaged, the files needed to load onto the SD card will be available in the <PetaLinux project directory>/images/linux directory. Before those files are copied over though, the SD card needs to be formatted in a particular manner.
The SD card needs three partitions: a 60MB minimum FAT32 partition, preceded by 4MB of free space, and the rest of the 8GB minimum card formatted as EXT4. FAT32 only has the address width available to index files up to 4GB in size, while EXT4 can index files up to 16TB. The items placed in the smaller FAT32 partition are the boot image, kernel, and device tree files which are smaller sized files compared to the root filesystem that could potentially be gigabytes in size (which is placed in the EXT4 partition). I believe the second stage bootloader (u-boot in this case) which in charge of loading the kernel and device tree is only capable of the same address width as FAT32, which is why the first partition is necessary and the whole SD card can't just be formatted as EXT4. I'm still researching into the topic, so I could be wrong about this.
You can partition the SD card from the command line, but I find it more convenient to do through a GUI application like gparted.
The BOOT.BIN (boot image file), image.ub (Linux kernel), and system.dtb (device tree) need to be copied to the FAT32 partition, and the rootfs.tar.gz needs to be extracted onto the EXT4 partition. I prefer to create two directories in the /media directory to mount the two different partitions to (named accordingly):
mkdir /media/BOOT
mkdir /media/rootfs
sudo mount /dev/sdc1 /media/BOOT
sudo mount /dev/sdc2 /media/rootfs
Then I simply use the cp command from the command line to copy over the files:
sudo cp /<petalinux project dir>/images/linux/BOOT.BIN /media/BOOT/
sudo cp /<petalinux project dir>/images/linux/image.ub /media/BOOT/
sudo cp /<petalinux project dir>/images/linux/system.dtb /media/BOOT/
And the tar command extract the root filesystem onto the EXT4 partition:
sudo tar xvf /<petalinux project dir>/images/linux/rootfs.tar.gz -C /media/rootfs/
Once complete, unmount both partitions before removing the SD card from the computer:
sudo umount /media/BOOT/
sudo umount /media/rootfs/
With the SD card installed in the Snickerdoodle, apply power and connect the microUSB port to your computer. Use your serial terminal application of choice to connect to the COM port now present. Once connected, press any key to halt the boot process and enter the u-boot environment because there is an edit that needs to be made.
The default boot command needs to be changed to the following to specify the order of how u-boot is to load the kernel and device tree into RAM during boot:
setenv default_bootcmd 'run cp_kernel2ram && run cp_dtb2ram && bootm ${netstart} - ${dtbnetstart}'
Save the new environment to the SD card:
saveenv
To resume the boot process simply pass boot to the u-boot command line.
The default login username is 'root' and password is 'root' if you didn't change it in the System Configuration menu. Once logged in, you can use ifconfig to assign a static IP address to each of the ethernet ports (the name of which will match whatever alias you assigned to it in the device tree) and set the netmask to match your local network.
sudo ifconfig eth0 192.168.1.10 netmask 255.255.0.0
sudo ifconfig eth1 192.168.1.11 netmask 255.255.0.0
I bought a cheap network switch that I have both Ethernet ports on the piSmahser hooked up to then to my laptop so I have my own little Snickerdoodle network!
All thoughts/opinions are my own and do not reflect those of any company/entity I currently/previously associate with.