Design Solutions Research & Design Hub

System Controller Manufacturing Test (Part 2)

Written by Nishant Mittal

The Software

In this two-part article Nishant discusses the design of an FPGA-based system controller built for testing and managing complex platforms. Part 1 focused on the hardware aspect of the system. In Part 2, he dives into the software side of the design, including the system’s power management and clock management.

In Part 1 of this article (Circuit Cellar 354, January 2019), we looked at the hardware design considerations to incorporate a system controller on our board. We covered Vivado designs implemented for a standard system controller setup. We also discussed the challenges confronted as we brainstormed our system controller hardware design and the interfaces needed for our complex platform requirements. Now that the hardware design is complete, here we’ll look at creating the “brain” for the system and bring it to life. With that in mind, in this article I’ll be discussing the software aspects of the system. This involves not only understanding the embedded system itself, but also the power management and clock management of the system. I’ll also discuss how to create a bootable PetaLinux image designed to run on the hardware design that we defined Part 1.

Figure 1 shows the architecture of the software design for the system controller. The architecture requires building a PetaLinux image that runs Linux on a Xilinx Zynq Ultrascale+ FPGA device. Doing this involves configuring the image with its proper settings, adding various programs for clock and power, enabling the drivers and then building the image. Let’s look at each of these steps one at the time.

FIGURE 1 – Block diagram of the system controller software design

Designing a bootable PetaLinux image is simple. It requires following few setup steps, adding your configurations and then building it. It’s recommended that you get the latest PetaLinux version from Assuming that you also have PetaLinux and Vivado downloaded, perform each of the following the steps to build the project.

If the board support package (BSP) which we created using techniques mentioned in part 1 is available 1, type: petalinux-create -t project -s <path to project> else run petalinux-create –type project –template zynqMP –name bootabletest on your Linux workstation.

This will create a folder named “bootabletest.” Note: the name of the image can be whatever you wish. Next, go into the bootabletest folder’s directory and type: petalinux-config –get-hw-description=/home/nishant/bootabletest/bin/. That opens the configuration window where various hardware and software settings can be done. We will do some minimal settings to make things work. In the GUI, go to “Yocto Settings” and enable “Debug Tweaks.” That will help when doing password-less logins.

Now go to Filesystem Packages > base and enable i2c-tools as shown in Figure 2. To add bash to the shell go back into to Filesystem Packages > base > shell and enable bash as shown in Figure 3. Bash is a necessary feature because it’s required for a majority of external world applications. You can also enable tar by going to Filesystem Packages and clicking tar.


Advertise Here

FIGURE 2 – i2c-tools enable
FIGURE 3 – bash enable

Keep in mind that the more features you enable, the larger the image size will be and the more build time it will take. The next important step is to edit the device tree according to specifications of the platform board. We’ll discuss the device tree for some devices throughout this article. Note that this article assumes you have some knowledge of Linux and device driver concepts. Open system-user.dtsi from project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi. In rootfs config, add the following items in Filesystem Packages > misc > python3. Here you can enable various Python library and JSON files.

The next step is to set up all the app configurations in order to create a Makefilebb file and .c file. These are the commands to create the apps:

petalinux-create -t apps –name setvoltage –enable
petalinux-create -t apps –name restorevoltage –enable
petalinux-create -t apps –name controlscript -enable

Note that those can be .c.c++, .sh or any other standard programming formats. Now that all the apps are added and the settings are done, the image is ready to be built. Using petalinux-build we can build the image. This will create image.ub and image files.

To create BOOT.BIN, run this command:
petalinux-package –boot –format BIN –fsbl images/linux/zynqmp_fsbl.elf –u-boot images/linux/u-boot.elf –pmufw images/linux/pmufw.elf –fpga images/linux/*.bit –atf images/linux/bl31.elf –force
(Note that the .bit file has to be added into the images folder in order to include the hardware details.)

You may need to reconfigure the project later on. With that in mind, it’s always a good idea to create a BSP file that you can use to recreate the project and make modifications into it. Create it with the command:

petalinux-package –bsp -p <projectroot> –output <yourname>.BSP

To learn more about PetaLinux, check out the PetaLinux user guide [1]. Next, we will discuss the power management and clock management interfaces.

Power management is an important aspect in a system controller configuration. PMBus is a derivative of the I2C protocol that allows power management via the I2C bus. A PMBus can be used to control, configure as well as monitor PMBus devices via I2C bus. Figure 4 shows the typical architecture of a PMBus device distribution implemented on a complex platform board. For our system, we’re using the TPS544B25 buck converter from Texas Instruments (TI) which can be controlled using PMBus.


Advertise Here

FIGURE 4 – PMBus device design

Figure 5 and Figure 6 show the data sequence required for the PMBus write operation. As shown in Figure 5, first a start signal is sent. Next the slave address is sent with a read/write (R/W) bit where 0 signifies write. When the acknowledge bit is high, command codes are sent. Command codes are various features for a rail. These include overvoltage max./min. limit, undervoltage limits, fault limits, required voltage, current min. and max. setting and so forth. Read sequence is same as write sequence, however it requires a repeated start in between the command code and slave address. Figure 6 shows the read sequence of the PMBus.

FIGURE 5 – Write sequence [2]
FIGURE 6 – Read sequence [2]

Listing 1 shows the data sequence for the TPS544B25 for sending data via IOCTL I2C for increasing the voltage. The sequence of write can be calculated based on the command code description in the datasheet [3], or based on TI’s Fusion Designer software, which gives an intuitive value evaluation for each command code. We get the value of each register setting by using TI’s PMBus tool. Figure 7 shows that tool’s configuration window. We then divide each 16 bits into eight bits each and write into the buffer.

WriteBuffer[0] = 0x40; //VOUT_OV_FAULT_LIMIT
WriteBuffer[2] = 0x02;
WriteBuffer[1] = 0x8F;
BytesWritten = write(Fdiic, WriteBuffer, 3);
WriteBuffer[0] = 0x42;//VOUT_OV_WARN_LIMIT
WriteBuffer[2] = 0x02;
WriteBuffer[1] = 0x8A;
BytesWritten = write(Fdiic, WriteBuffer, 3);
WriteBuffer[0] = 0x24;//VOUT_MAX
WriteBuffer[2] = 0x03;
WriteBuffer[1] = 0x00;
BytesWritten = write(Fdiic, WriteBuffer, 3);
WriteBuffer[0] = 0x44; //VOUT_UV_FAULT_LIMIT
WriteBuffer[2] = 0x01;
WriteBuffer[1] = 0x00;
BytesWritten = write(Fdiic, WriteBuffer, 3);
WriteBuffer[0] = 0x43; //VOUT_UV_WARN_LIMIT
WriteBuffer[2] = 0x01;
WriteBuffer[1] = 0x1A;
BytesWritten = write(Fdiic, WriteBuffer, 3);
WriteBuffer[0] = 0xA4; //MFR_VOUT_MIN
WriteBuffer[2] = 0x01;
WriteBuffer[1] = 0x00;
BytesWritten = write(Fdiic, WriteBuffer, 3);
WriteBuffer[0] = 0x21;//VOUT_COMMAND
WriteBuffer[2] = (m>>8) & 0xff;
WriteBuffer[1] = m & 0x00ff;
BytesWritten = write(Fdiic, WriteBuffer, 3);

LISTING 1 – Shown here is the data sequence for the TPS544B25 in terms of sending data via IOCTL I2C for increasing the voltage.

The device tree descriptor for the TPS544B25 is as follows:

[email protected] {
compatible = “tps544b25”;
reg = <0x0A>;

We selected Silicon Labs’ SI570 device for clock management and control. The SI570 is considered to offer low clock jitter with a frequency variation from 10MHz to 945MHz. SI570 drivers are readily available. The SI570’s device driver entry has to be done in the device tree along with the initial default frequency, which has been configured in the SI570’s on-chip NVRAM.

For the SI570, the system-user.dtsi should be:

si570_1: [email protected] { /* USER SI570 - u42 */
#clock-cells = <0>;
compatible = “silabs,si570”;
reg = <0x5d>;
temperature-stability = <50>;
factory-fout = <156250000>;
clock-frequency = <300000000>;
clock-output-names = “si570_user”;}

After booting is completed, verify the registered clock frequency by SI570 by writing:

[email protected]:~# dmesg | grep “si570”
[ 4.009814] si570 6-005d: registered, current frequency 300000000 Hz

You can read the current clock frequency of a particular SI570 device using:

cat /sys/devices/platform/ref_clk/set_rate

You can also set the clock frequency of your choice using:

echo 33333333 > /sys/devices/platform/ref_clk/set_rate


Advertise Here

That sets the frequency to 33.33MHz.

Now that the image is ready, and we have BOOT.BIN and image.ub file saved on a formatted SD card, we can boot the image by inserting the SD card to the board and powering it up. Assuming that the board has an eMMC loaded on it, we can follow a step-by-step procedure to mount the eMMC and transfer the files from the SD card to the eMMC and then boot the image. eMMC has number of advantages because it’s attached onto the board and less prone to damage compared to an SD card. First you boot the SD card using BOOT.BIN and image.ub on the SD card. Once the Linux is booted, follow the steps detailed in the sidebar “Booting the Image” below.

In this two-part article, we’ve explored various aspects of designing system controller using Xilinx’s Zynq Ultra scale plus FPGA from both software and hardware point of view. We looked at creating a Vivado design for the system controller, how to constraint the files and how to plan the interfaces. We also discussed how to create a bootable PetaLinux image that can work with all the of the Zynq Ultrascale+’s hardware specifications. Lastly, talked about using PMBus communication for power management, explored the clock management aspects of the design, and examined the device tree descriptors for the devices we used. We also looked at ways to boot the image using SD card and eMMC. 


Follow these steps to mount the eMMC and transfer the files from the SD card to the eMMC and then boot the image (use command m for help).

1) Type umount /dev/mmcblk0p1
Note: If the device is not partitioned and and it is a new one then it may show an invalid or not found command.

2) Type umount /mnt/
Note: that if the device is not partitioned and it is a new one then it may show an invalid or not found command.

3) Now type:
dd if=/dev/zero of=”\dev\mmcblk0” bs=1024 count=1 and this will print:
1+0 records in
1+0 records out

4) Now type fdisk /dev/mmcblk0 and this will print:
The number of cylinders for this disk is set to 485632.

There is nothing wrong with that, but this is larger than 1024 and could in certain setups cause problems with either (a) software that runs at boot time (for example, old versions of LILO), or (b) booting and partitioning software from other Oss (for example, DOS FDISK, OS/2 FDISK).

5) Type p

6) The above step (#5) will show you if the device partition is available and will show you its size in gigabits. If it shows partition available, then type d.

7) If at step 6 you deleted a partition, type w so that the changes can be saved.

8) If there was no partition, skip steps 6 and 7.

9) Now type n and this will print:
Partition type
p primary partition (1-4)
e extended

10) Type p and this will print:
Partition number (1-4):

11) Type 1 and this will print:
First sector (16-31080447, default 16):

12) Press enter (without typing anything) and this
will print:
Using default value 16
Last sector or +size{,K,M,G,T} (16-31080447, default 31080447):

13) Type +200M and this will print:
Command (m for help):

14) Type v and this will print:
30670847 unallocated sectors

15) Type w and this will print:
The partition table has been altered.
Calling ioctl() to re-read partition table
[ 259.743828] mmcblk0: p1

Note that in this step if you see any message that the device may be corrupt or not unmounted, you may have not followed the steps or may have not unmounted properly. Repeat from step #1 if you see one.

16) Type mkfs.vfat -F 32 -n boot /dev/mmcblk0p1

17) Type mount /dev/mmcblk0p1 /mnt/

18) Type cd /mnt

19) Type cp /run/media/mmcblk1p1/BOOT.BIN ./

20) Type cp /run/media/mmcblk1p1/image.ub ./

21) Type cd /

22) Type sync

23) Type umount /mnt/

24) Power 0

25) Change boot mode to emmc

26) Power 1

27) Linux BOOTED



[1] Petalinux user guide:

Silicon Labs |
Texas Instruments |
Xilinx |


Don't miss out on upcoming issues of Circuit Cellar. Subscribe today!

Note: We’ve made the October 2017 issue of Circuit Cellar available as a free sample issue. In it, you’ll find a rich variety of the kinds of articles and information that exemplify a typical issue of the current magazine.

Would you like to write for Circuit Cellar? We are always accepting articles/posts from the technical community. Get in touch with us and let's discuss your ideas.

Become a Sponsor
Systems Engineer | + posts

Nishant Mittal is a Hardware Systems Engineer in Hyderabad, India.