In this blog series, I dive into Xilinx's new platform management utility called xmutil.
Introduction
In my last blog, I gave a high level overview of the xmutil utility.
XMUTIL ... a new platform and app management utility from Xilinx
We already saw that xmutil is a shell script, that calls the following utilities as sub-commands:
sub-commands | utility | reference |
---|---|---|
boardid | fru_print.py | ? |
bootfw_status bootfw_update | image_update | https://github.com/Xilinx/linux-image_update |
listapps unloadapp loadapp | dfx-mgr-client | https://github.com/Xilinx/dfx-mgr |
platformstats | platformstats | https://github.com/Xilinx/platformstats |
ddrqos | ddr-qos | https://github.com/Xilinx/ddr-qos |
axiqos | axi-qos | https://github.com/Xilinx/axi-qos |
pwrctl | som-pwr-ctl | https://github.com/Xilinx/xmutil/blob/release-2020.2.2_k26/som-pwrctl |
In this blog, I start investigating how to port this utility to a custom platform (in this case, Ultra96-V2), starting with the platformstats sub-command / utility.
Installing xmutil on Ultra96-V2
In order to understand how the xmutil works, lets install the xmutil and platformstats components to the Ultra96-V2 board.
Note that I am using my Vitis-AI 1.3 image for Ultra96-V2 for this exercise :
http://avnet.me/vitis-ai-1.3-project
On our embedded Ultra06-V2 platform, let's start by creating a xmutil directory to work with:
mkdir xmutil
cd xmutil
Next, let's clone the xmutil repository, and install the shell script to the /usr/bin directory:
git clone https://github.com/Xilinx/xmutil
cd xmutil
cp xmutil /usr/bin/.
cd ..
Finally, let's clone the platformstats repository, build the utility, and install to the expected location (I figured this out with trial and error):
git clone -b avnet-2020.2 https://github.com/Xilinx/platformstats
cd platformstats
./build.sh
cp src/libplatformstats.so.1.0 /usr/lib/.
ln -s /usr/lib/libplatformstats.so.1.0 /usr/lib/libplatformstats.so.1
ln -s /usr/lib/libplatformstats.so.1 /usr/lib/libplatformstats.so
cp platformstats /usr/bin/.
cd ..
If we run the platformstats sub-command via xmutil, with the "-a" argument, we get the following output:
root@u96v2-sbc-base-2020-2:~/xmutil/platformstats# xmutil platformstats -a
CPU Utilization
CPU0 : 6.222449%
CPU1 : 0.100000%
CPU2 : 0.100000%
CPU3 : 0.100000%
RAM Utilization
MemTotal : 2036740 kB
MemFree : 1513088 kB
MemAvailable : 1717784 kB
Swap Mem Utilization
SwapTotal : 0 kB
SwapFree : 0 kB
Power Utilization
no hwmon device found for ina260_u14 under /sys/class/hwmon
no hwmon device found for ams under /sys/class/hwmon
CMA Mem Utilization
CmaTotal : 0 kB
CmaFree : 0 kB
CPU Frequency
match not foundCPU0 : 0.000000 MHz
match not foundCPU1 : 0.000000 MHz
Most of the code seems to be generic, making use of the standard linux file system.
platformstats option | description | reference |
---|---|---|
-c | CPU Utilization | /proc/stat |
-r | RAM Utilization | /proc/meminfo (lines 1-3) |
-s | Swap Mem UItilization | /proc/meminfo (lines 15-16) |
-p | Power Utilization | /sys/class/hwmon/hwmon*/* |
-m | CMA Memory Utilization | /proc/meminfo (lines 38-39) |
-f | CPU Frequency | /proc/cpuinfo |
The "CPU Frequency" hangs, and I don't know why. I noticed that is also hangs on the KV260.
The "Power Utilization" is not working on Ultra96-V2, and is looking for "ina260", "ams" devices specific to the KV260.
root@u96v2-sbc-base-2020-2:~/xmutil/platformstats# xmutil platformstats -p
Power Utilization
no hwmon device found for ina260_u14 under /sys/class/hwmon
no hwmon device found for ams under /sys/class/hwmon
For a custom board, therefore, this option needs to be modified.
Modifying platformstats for Ultra96-V2
I modified the code for the avnet platforms. These modifications were based on the work I had previously done with the python based project:
https://www.hackster.io/AlbertaBeef/monitoring-power-on-the-avnet-platforms-e52a23
For the Ultra96-V2, I specified the power monitoring devices available on the pmbus as a data structure.
Note that I also included a few temperature values, which should probably be included with a new option ( -t => Temperature ).
struct pmbus_info pmbus_ultra96v2[] =
{
// device address name label alias unit division
// ir38060-i2c-6-45
{ "ir38060", "6-0045", "", "pout1", " 5V", "mW", 1000 },
{ "ir38060", "6-0045", "", "iout1", " 5V", "mA", 1 },
{ "ir38060", "6-0045", "", "iout1", " 5V", "mV", 1 },
{ "ir38060", "6-0045", "temp1_input", "temp1", "Temperature", "C", 1000 },
// irps5401-i2c-6-43
{ "irps5401", "6-0043", "", "pout1", " VCCAUX", "mW", 1000 },
{ "irps5401", "6-0043", "", "pout2", " VCCO 1.2V", "mW", 1000 },
{ "irps5401", "6-0043", "", "pout3", " VCCO 1.1V", "mW", 1000 },
{ "irps5401", "6-0043", "", "pout4", " VCCINT", "mW", 1000 },
{ "irps5401", "6-0043", "", "pout5", " 3.3V DP", "mW", 1000 },
{ "irps5401", "6-0043", "temp1_input", "temp1", "Temperature", "C", 1000 },
// irps5401-i2c-6-44
{ "irps5401", "6-0044", "", "pout1", " VCCPSAUX", "mW", 1000 },
{ "irps5401", "6-0044", "", "pout2", " PSINT_LP", "mW", 1000 },
{ "irps5401", "6-0044", "", "pout3", " VCCO 3.3V", "mW", 1000 },
{ "irps5401", "6-0044", "", "pout4", " PSINT_FP", "mW", 1000 },
{ "irps5401", "6-0044", "", "pout5", " PSPLL 1.2V", "mW", 1000 },
{ "irps5401", "6-0044", "temp1_input", "temp1", "Temperature", "C", 1000 },
//
{ "", "", "", "", "", 1 }
};
I did the same for the UltraZed-EVCC:
struct pmbus_info pmbus_uz7ev_evcc[] =
{
// device address name label alias unit division
// ir38063-i2c-3-4c
{ "ir38063", "3-004c", "", "pout1", " Carrier 3V3", "mW", 1000 },
// ir38063-i2c-3-4b
{ "ir38063", "3-004b", "", "pout1", " Carrier 1V8", "mW", 1000 },
// irps5401-i2c-3-4a
{ "irps5401", "3-004a", "", "pout1", " Carrier 0V9 MGTAVCC", "mW", 1000 },
{ "irps5401", "3-004a", "", "pout2", " Carrier 1V2 MGTAVTT", "mW", 1000 },
{ "irps5401", "3-004a", "", "pout3", " Carrier 1V1 HDMI", "mW", 1000 },
//{ "irps5401", "3-004a", "", "pout4", " Unused", "mW", 1000 },
{ "irps5401", "3-004a", "", "pout5", "Carrier 1V8 MGTVCCAUX LDO", "mW", 1000 },
// irps5401-i2c-3-49
{ "irps5401", "3-0049", "", "pout1", " Carrier 0V85 MGTRAVCC", "mW", 1000 },
{ "irps5401", "3-0049", "", "pout2", " Carrier 1V8 VCCO", "mW", 1000 },
{ "irps5401", "3-0049", "", "pout3", " Carrier 3V3 VCCO", "mW", 1000 },
{ "irps5401", "3-0049", "", "pout4", " Carrier 5V MAIN", "mW", 1000 },
{ "irps5401", "3-0049", "", "pout5", " Carrier 1V8 MGTRAVTT LDO", "mW", 1000 },
{ "irps5401", "3-0049", "temp1_input", "temp1", " Temperature", "C", 1000 },
// ir38063-i2c-3-48
{ "ir38063", "3-0048", "", "pout1", " SOM 0V85 VCCINT", "mW", 1000 },
// irps5401-i2c-3-47
{ "irps5401", "3-0047", "", "pout1", " SOM 1V8 VCCAUX", "mW", 1000 },
{ "irps5401", "3-0047", "", "pout2", " SOM 3V3", "mW", 1000 },
{ "irps5401", "3-0047", "", "pout3", " SOM 0V9 VCUINT", "mW", 1000 },
{ "irps5401", "3-0047", "", "pout4", " SOM 1V2 VCCO_HP_66", "mW", 1000 },
{ "irps5401", "3-0047", "", "pout5", " SOM 1V8 PSDDR_PLL LDO", "mW", 1000 },
{ "irps5401", "3-0047", "temp1_input", "temp1", " Temperature", "C", 1000 },
// irps5401-i2c-3-46
{ "irps5401", "3-0046", "", "pout1", " SOM 1V2 VCCO_PSIO", "mW", 1000 },
{ "irps5401", "3-0046", "", "pout2", " SOM 0V85 VCC_PSINTLP", "mW", 1000 },
{ "irps5401", "3-0046", "", "pout3", " SOM 1V2 VCCO_PSDDR4_504", "mW", 1000 },
{ "irps5401", "3-0046", "", "pout4", " SOM 0V85 VCC_PSINTFP", "mW", 1000 },
{ "irps5401", "3-0046", "", "pout5", " SOM 1V2 VCC_PSPLL LDO", "mW", 1000 },
{ "irps5401", "3-0046", "temp1_input", "temp1", " Temperature", "C", 1000 },
//
{ "", "", "", "", "", 1 }
};
And the same for the UltraZed-EG IOCC and PCIE carrier cards:
struct pmbus_info pmbus_uz3eg_xxx[] =
{
// device address name label alias unit division
// irps5401-i2c-3-43
{ "irps5401", "3-0043", "", "pout1", " PSIO", "mW", 1000 },
{ "irps5401", "3-0043", "", "pout2", " VCCAUX", "mW", 1000 },
{ "irps5401", "3-0043", "", "pout3", " PSINTLP", "mW", 1000 },
{ "irps5401", "3-0043", "", "pout4", " PSINTFP", "mW", 1000 },
{ "irps5401", "3-0043", "", "pout5", " PSPLL", "mW", 1000 },
{ "irps5401", "3-0043", "temp1_input", "temp1", "Temperature", "C", 1000 },
// irps5401-i2c-3-44
{ "irps5401", "3-0044", "", "pout1", " PSDDR4", "mW", 1000 },
{ "irps5401", "3-0044", "", "pout2", " INT_IO", "mW", 1000 },
{ "irps5401", "3-0044", "", "pout3", " 3.3V", "mW", 1000 },
{ "irps5401", "3-0044", "", "pout4", " INT", "mW", 1000 },
{ "irps5401", "3-0044", "", "pout5", " PSDDRPLL", "mW", 1000 },
{ "irps5401", "3-0044", "temp1_input", "temp1", "Temperature", "C", 1000 },
// irps5401-i2c-3-45
{ "irps5401", "3-0045", "", "pout1", " MGTAVCC", "mW", 1000 },
{ "irps5401", "3-0045", "", "pout2", " 5V", "mW", 1000 },
{ "irps5401", "3-0045", "", "pout3", " 3.3V", "mW", 1000 },
{ "irps5401", "3-0045", "", "pout4", " VCCO 1.8V", "mW", 1000 },
{ "irps5401", "3-0045", "", "pout5", " MGTAVTT", "mW", 1000 },
{ "irps5401", "3-0045", "temp1_input", "temp1", "Temperature", "C", 1000 },
//
{ "", "", "", "", "", 1 }
};
The print_pmbus_info function scans the /sys/class/hwmon to find the matching pmbus device, address, and label, then displays the value. I admit the code is not very clean, but gets the job done.
Finally, I use the hostname command to determine which Avnet board we are running on:
int print_power_utilization(int verbose_flag)
{
FILE *fp;
char hostname[128] = "";
fp = popen("hostname", "r");
if (fp != NULL) {
if ( fgets(hostname, sizeof(hostname), fp) != NULL ) {
if (verbose_flag)
{
printf("hostname=%s\n", hostname);
}
}
pclose(fp);
}
if ( strstr(hostname,"u96v2") )
{
if (verbose_flag)
{
printf("Ultra96-V2\n");
}
print_pmbus_info(verbose_flag,pmbus_ultra96v2);
}
if ( strstr(hostname,"uz7ev") )
{
if (verbose_flag)
{
printf("UltraZed-7EV-EVCC\n");
}
print_pmbus_info(verbose_flag,pmbus_uz7ev_evcc);
}
if ( strstr(hostname,"uz3eg") )
{
if (verbose_flag)
{
printf("UltraZed-3EG\n");
}
print_pmbus_info(verbose_flag,pmbus_uz3eg_xxx);
}
return(0);
}
Let's clone this version of the platformstats repository, re-build, and re-install:
git clone -b avnet-2020.2 https://github.com/AlbertaBeef/platformstats platformstats_avnet
cd platformstats_avnet
./build.sh
cp src/libplatformstats.so.1.0 /usr/lib/.
ln -s /usr/lib/libplatformstats.so.1.0 /usr/lib/libplatformstats.so.1
ln -s /usr/lib/libplatformstats.so.1 /usr/lib/libplatformstats.so
cp platformstats /usr/bin/.
cd ..
Now if we run the platformstats sub-command via xmutil on the Ultra96-V2 board, we get the following:
root@u96v2-sbc-base-2020-2:~/xmutil/platformstats_avnet# xmutil platformstats -p
Power Utilization:
ir38060@6-0045-pout1 ( 5V) = 5062 mW
ir38060@6-0045-iout1 ( 5V) = 1187 mA
ir38060@6-0045-iout1 ( 5V) = 1187 mV
ir38060@6-0045-temp1 (Temperature) = 61 C
irps5401@6-0043-pout1 ( VCCAUX) = 156 mW
irps5401@6-0043-pout2 ( VCCO 1.2V) = 0 mW
irps5401@6-0043-pout3 ( VCCO 1.1V) = 125 mW
irps5401@6-0043-pout4 ( VCCINT) = 1218 mW
irps5401@6-0043-pout5 ( 3.3V DP) = 0 mW
irps5401@6-0043-temp1 (Temperature) = 54 C
irps5401@6-0044-pout1 ( VCCPSAUX) = 281 mW
irps5401@6-0044-pout2 ( PSINT_LP) = 250 mW
irps5401@6-0044-pout3 ( VCCO 3.3V) = 500 mW
irps5401@6-0044-pout4 ( PSINT_FP) = 656 mW
irps5401@6-0044-pout5 ( PSPLL 1.2V) = 46 mW
irps5401@6-0044-temp1 (Temperature) = 56 C
What Next ?
Which sub-command should we investigate next ?