Important
This workaround is applicable to the VCK190, VMK180 and VPK120 because they all use the same circuitry for generating the VADJ voltage: an IR38164 buck regulator with the same I2C address (0x1E), connecting to the same port of the same I2C switch (address 0x74, port 0) and connecting to the same I2C pins of the Versal device (PMC MIO46/47).
Note that this workaround is NOT applicable to the VEK280 because that board applies a fixed VADJ voltage of 1.5VDC.
VADJ issue with Versal boards
If you’ve taken a good look at the VCK190 or VMK180 Versal Evaluation boards, you would have noticed that it has a Zynq UltraScale+ device on it. This would be the central device on a lower cost development board, but on these boards it is just a lowly system controller. The system controller is there to manage power supplies, clocks and other on-board resources and it runs independently of the main device. Most AMD evaluation boards have a system controller but it’s not always implemented on a Zynq UltraScale+. The system controller is out-of-sight and out-of-mind most of the time, but when you switch on that eval board, the main FPGA/MPSoC/APAC device is not the only thing that is going through a boot sequence.
One of the roles of the system controller is to enable the FMC’s adjustable I/O voltage: VADJ. The way that it normally does this is by reading from an EEPROM on the connected FMC card (or cards), then working out a voltage that satisfies all the connected FMC cards and the limitations of the main FPGA/MPSoC/APAC device. When a suitable voltage has been determined, it configures the voltage regulator to generate that voltage. Simple job. Nothing to see here. As long as the system controller can enable VADJ sooner than it is required by the main device, it’s all smooth sailing…
Then came the BEAM tool
On earlier boards, such as the KCU105, ZCU102, etc, this system for enabling VADJ worked fine, because the system controller was implemented by a bare metal application - fast enough to enable the VADJ voltage before the main device’s boot sequence got going. On the newer VCK190 and VMK180 boards, this has all changed with the introduction of a system controller that is PetaLinux based. It’s called the BEAM Tool. Being PetaLinux based it’s got a lot more functionality: it runs a webserver, it has a Board Interface Test, it can interface with a desktop application. This new style system controller really adds value and is going to change the way we work with AMD evaluation kits. I really do think that it’s great. However, when it comes to enabling VADJ, this new PetaLinux based system controller is just too slow.
With the BEAM Tool, you have the option to enable VADJ to a specific voltage on boot, or to set the voltage based on the EEPROM of the connected FMC cards. Either way, the VADJ voltage only gets enabled once the BEAM Tool has gone through it’s entire boot sequence. If you’ve worked with PetaLinux before, you know that the boot sequence can take 30 seconds, maybe a minute? Don’t forget, at the same time, the Versal device is going through it’s own boot sequence, and all without any power to the I/Os that connect to the FMC cards. That doesn’t work.
Potential solutions
When I first came across this problem, these were the potential solutions that I could think of:
- Delay the Versal boot sequence until the system controller boot sequence was complete
- Develop a baremetal system controller for the Zynq UltraScale+
- Enable VADJ from the bootloader of the Versal
The first solution seemed natural to me, but I don’t know if it was even possible, and I didn’t like the idea of extending the total boot time. The second solution seemed like a lot of work to fix a small problem, so I didn’t give it much thought. The third solution looked like being the easiest one to implement while having the advantage that it could be independent of the system controller altogether. So that’s what I did.
Enabling VADJ via U-Boot
Once I was able to figure out the appropriate register configurations for the IR38164 buck regulator, enabling VADJ became
a simple list of I2C writes that could be done from U-Boot. We take that list of I2C write commands and put them in an environment
variable for U-Boot, then we overwrite the boot command to run this environment variable before running the main boot command.
In effect, this comes down to appending the following code to the platform-top.h
file, located in
project-spec/meta-user/recipes-bsp/u-boot/files
of the PetaLinux project:
#define IR38164_VADJ_SETTINGS \
"vadj_1v2_en=" \
"i2c dev 0;" \
"i2c mw 0x74 0x00.0 0x01 0x1;" \
"i2c mw 0x1E 0x24.1 0x01 0x1;" \
"i2c mw 0x1E 0x25.1 0x33 0x1;" \
"i2c mw 0x1E 0x3A.1 0x01 0x1;" \
"i2c mw 0x1E 0x3B.1 0x8F 0x1;" \
"i2c mw 0x1E 0x3D.1 0x01 0x1;" \
"i2c mw 0x1E 0x3E.1 0x8F 0x1;" \
"i2c mw 0x1E 0x3F.1 0x00 0x1;" \
"i2c mw 0x1E 0x40.1 0x00 0x1;" \
"i2c mw 0x1E 0x41.1 0x00 0x1;" \
"i2c mw 0x1E 0x42.1 0x00 0x1;" \
"i2c mw 0x1E 0x22.1 0x80 0x1\0" \
"vadj_1v5_en=" \
"i2c dev 0;" \
"i2c mw 0x74 0x00.0 0x01 0x1;" \
"i2c mw 0x1E 0x24.1 0x01 0x1;" \
"i2c mw 0x1E 0x25.1 0x80 0x1;" \
"i2c mw 0x1E 0x3A.1 0x01 0x1;" \
"i2c mw 0x1E 0x3B.1 0xF3 0x1;" \
"i2c mw 0x1E 0x3D.1 0x01 0x1;" \
"i2c mw 0x1E 0x3E.1 0xF3 0x1;" \
"i2c mw 0x1E 0x3F.1 0x00 0x1;" \
"i2c mw 0x1E 0x40.1 0x00 0x1;" \
"i2c mw 0x1E 0x41.1 0x00 0x1;" \
"i2c mw 0x1E 0x42.1 0x00 0x1;" \
"i2c mw 0x1E 0x22.1 0x80 0x1\0" \
#define CONFIG_EXTRA_ENV_SETTINGS \
ENV_MEM_LAYOUT_SETTINGS \
IR38164_VADJ_SETTINGS \
BOOTENV
#define CONFIG_BOOTCOMMAND "run vadj_1v2_en; run distro_bootcmd"
The above is going to configure VADJ for 1.2V, but it can be easily changed to 1.5V by setting the CONFIG_BOOTCOMMAND
to
run vadj_1v5_en; run distro_bootcmd
.
If you make this change to a PetaLinux project, the VADJ voltage is guaranteed to be enabled before the Versal starts it’s PetaLinux boot sequence; and this works regardless of the system controller running or not. One thing to keep in mind: if you use this workaround with the BEAM Tool/system controller running, be sure that the system controller has not been configured to set VADJ to a different voltage on boot. For example, if you use this workaround to have U-Boot set VADJ to 1.2V, then you don’t want the system controller to be configured to set VADJ to 1.5V (or 0V!) on boot, because it will apply that setting, albeit some 30 seconds later.
Update 2024-10-02
The above code to be appended to platform-top.h
was written for PetaLinux release 2022.1. To use this in release 2024.1,
you need to change CONFIG_EXTRA_ENV_SETTINGS
to CFG_EXTRA_ENV_SETTINGS
. The reason is that the label was changed in
the xilinx_versal.h
include file for 2024.1. Below I have included the links to a few different versions of that file
and the label that you should use for that version:
Version | Github link | Extra settings define label |
---|---|---|
2022.1 | xilinx_versal.h | CONFIG_EXTRA_ENV_SETTINGS |
2023.1 | xilinx_versal.h | CONFIG_EXTRA_ENV_SETTINGS |
2024.1 | xilinx_versal.h | CFG_EXTRA_ENV_SETTINGS |
If you don’t use the right label, then U-Boot will give you the message: ## Error: "vadj_1v2_en" not defined
.
This is because by using the wrong label, the extra environment variables never get added to U-Boot.