The PMU firmware
The Zynq UltraScale+ MPSoC, or simply ZynqMP for brevity, is a powerful and complex chip by Xilinx based on ARM cores and an FPGA. Within that chip, the PMU (Platform Management Unit) is a Microblaze processor that handles power states, clock and power domains and other very low-level tasks. It obviously has a crucial role for the chip.
There is no “standard” PMU firmware image: every user is supposed to build a custom one. Luckily its code is open source, and publicly available on the Xilinx Embeddedsw repository.
Unfortunately building a PMU firmware can be a bit annoying. One of the reasons is that it’s the only programmable Microblaze core in a chip that otherwise has only ARM cores. As such it needs an appropriate toolchain.
One of the solutions is to built it using the Xilinx XSDK, a firmware IDE by Xilinx. But it is a heavyweight tool just to build a 128 kB firmware, and it’s non-trivial to automate in a build process.
The other mainstream option is to let Yocto build it, which is a natural choice if you are using it to build the rest of your system. Some obscure rules are available in the meta-xilinx layer to build a Microblaze toolchain in an otherwise ARM64 configuration, and then use it to build the PMU firmware and its dependencies. This is a powerful method, but it makes it hard to understand how the firmware gets built. It also requires to build a toolchain and several packages at every build from scratch.
Booting with U-Boot SPL
Things get even more complex if you want to boot using U-Boot and its SPL, and here’s why.
To know how the chip resources are to be managed, the PMU firmware needs a “configuration object” to be passed from the outside even before U-Boot proper can start. The Xilinx First Stage Bootloader (FSBL) is able to load the configuration object. But using FSBL has drawbacks, including a poor boot speed.
The obvious alternative is U-Boot SPL, which is fast and the de facto standard in the embedded Linux ecosystem. Except it is currently unable to pass the configuration object to the PMU.
The current best hack to boot with U-Boot SPL is to modify the PMU firmware source code to load a configuration object hard-coded in its own image. This removes the need for the SPL to load it. Implementing this hack requires additional build-time operations, and is implemented for example in the meta-topic Yocto layer. This hack works, although it forces to build a different PMU firmware for every different configuration (e.g. every board), but at the moment there’s not much that can be done. Moreover it adds to the complexity of the build process, since these hacks are not implemented in the meta-xilinx Yocto layer.
Keep it simple, stupid!
Does building a PMU firmware really need to be that complex? Of course not!
So in order to document and simplify the whole process, including the above mentioned hack to boot with U-Boot SPL, I wrote a shell script that does it in the simplest possible way. The design goals for this script are being simple, have minimal dependencies and to be easy to understand and modify.
The resulting script is even simpler than I expected! It also works without the need to setup a Yocto environment or any Xilinx IDE: actually it only needs crosstool-NG to build the Microblaze toolchain and the PMU firmware source code. It also allows to build a toolchain once and reuse it for every build, meaning a different PMU firmware image can be rebuilt in less than 10 seconds.
I published this script on the zynqmp-pmufw-builder GitHub repository. Feel free to use it for your projects, or simply to understand what’s going on when you use other tools!
7 thoughts on “Building the ZynqMP PMU firmware, the simple way”
This script has been a life-saver. I struggled for weeks to sort out the complexity of Xilinx Yocto layers and building PMU firmware. It’s amazing how much simpler it can be. Thanks for your effort to solve this problem.
after I had sorted out that complexity it seemed natural to share the result, from the point of view of an open-source enthusiast.
I’m glad to know it is still useful to somebody.
You might also be interested in reading about the further improvements around booting ZynqMP with U-Boot SPL in this other post: https://lucaceresoli.net/zynqmp-uboot-spl-pmufw-cfg-load/
I followed the steps in https://github.com/lucaceresoli/zynqmp-pmufw-builder. After step 2 I have pmu_cfg_obj.c in two different directories (./embeddedsw/lib/sw_apps/zynqmp_fsbl/misc/pm_cfg_obj.c and ./embeddedsw/lib/sw_apps/zynqmp_pmufw/src/pm_cfg_obj.c).
I tried to ran anyway the step 4 but it returns:
patching file lib/sw_apps/zynqmp_pmufw/src/pm_binding.c
Hunk #1 succeeded at 23 (offset -25 lines).
Hunk #2 succeeded at 65 (offset -25 lines).
sed: impossibile leggere pm_cfg_obj.c: File o directory non esistente
Should I copy one of the two files mentioned above in current dir? And then, should I rerun ./build.sh pmufw-patch?
Thanks. I need to find a way to fix pmu-firmuware issue to create a yocto build for zcu102.
patching the PMUFW sources to use an hard-coded configuration object is not supported anymore. There is a better way, see https://lucaceresoli.net/zynqmp-uboot-spl-pmufw-cfg-load/ for more info. Apologies, I should have updated README.md, I’ll do it now.
BTW you probably can still use the old, unsupported way by copying your pm_cfg_obj.c in the root of your zynqmp-pmufw-builder clone.
What matters it that you must use the pm_cfg_obj.c for _your_ hardware and configuration. If you are using a supported development board _and_ you are using it with the same configuration as the vendor, then you can use a pm_cfg_obj.c provided by the vendor or found in the U-Boot sources, but to be generic you should produce that file for your board and configuration. i.e. from your Vivado project.
I know only one way to produce it: take the XSA (or HDF) file generated by Vivado and open it with Vitis (or XSDK), then follow the wizard to create a FSBL project. You can find your pm_cfg_obj.c somewhere in the generated files: take that file and throw away the rest.
Hope it helps.
I tried again following your updated README.md, after I pulled your git.
When I ran “$ ./build.sh pmufw-build” I encountered an error:
microblaze_selftest.S: Assembler messages:
microblaze_selftest.S:554: Error: unknown opcode “bsifi”
microblaze_selftest.S:555: Error: unknown opcode “bsefi”
make: *** [Makefile:61: ../../../lib/microblaze_selftest.o] Error 1
make: Leaving directory ‘/home/raffaele/yocto/ultra-scale/zynqmp-pmufw-builder/embeddedsw/lib/sw_apps/zynqmp_pmufw/misc/zynqmp_pmufw_bsp/psu_pmu_0/libsrc/standalone/src’
I think an issue or bug report is more effectively reported as a github issue.
However, to reply your question: I just tested building and it succeeds. However I tried more recent PMUFW versions (2020.2) instead of 2020.1 that is currently supported by zynqmp-pmufw-builder, and that failed with the same error message. Are you trying to build PMUFW >= 2020.2?
I will try to look at these versions, but that won’t be very soon I’m afraid. If you solve it before I do, please send a patch or pull request!
In case you still need it: current zynqmp-pmufw-builder is able to build pmufw 2021.x and zynqmp-pmufw-binaries has a prebuilt pmufw-v2021.2.