PMBus on the ZCU104

The ZCU104 has some support for monitoring power rails on the board using PMBus. PYNQ exposes these rails through the get_rails function that returns a dictionary of all of the rails available to be monitored.


In [1]:
import pynq

rails = pynq.get_rails()
rails


Out[1]:
{'12V': Rail {name=12V, voltage=Sensor {name=12V_voltage, value=12.113V}, current=Sensor {name=12V_current, value=1.159A}, power=Sensor {name=12V_power, value=14.05W}},
 '1V13': Rail {name=1V13, voltage=Sensor {name=1V13_voltage, value=1.125V}, current=Sensor {name=1V13_current, value=0.218A}, power=Sensor {name=1V13_power, value=0.21875W}},
 '1V2': Rail {name=1V2, voltage=Sensor {name=1V2_voltage, value=1.203V}, current=Sensor {name=1V2_current, value=0.234A}, power=Sensor {name=1V2_power, value=0.28125W}},
 '1V8': Rail {name=1V8, voltage=Sensor {name=1V8_voltage, value=1.804V}, current=Sensor {name=1V8_current, value=0.328A}, power=Sensor {name=1V8_power, value=0.5625W}},
 '3V3': Rail {name=3V3, voltage=Sensor {name=3V3_voltage, value=3.304V}, current=Sensor {name=3V3_current, value=0.625A}, power=Sensor {name=3V3_power, value=2.0W}},
 '5V0': Rail {name=5V0, voltage=Sensor {name=5V0_voltage, value=5.0V}, current=Sensor {name=5V0_current, value=0.0A}, power=Sensor {name=5V0_power, value=0.0W}},
 'FMC': Rail {name=FMC, voltage=Sensor {name=FMC_voltage, value=0.156V}, current=Sensor {name=FMC_current, value=10.187A}, power=Sensor {name=FMC_power, value=0.03125W}},
 'INT': Rail {name=INT, voltage=Sensor {name=INT_voltage, value=0.851V}, current=Sensor {name=INT_current, value=4.375A}, power=Sensor {name=INT_power, value=3.5W}},
 'MGTA': Rail {name=MGTA, voltage=Sensor {name=MGTA_voltage, value=0.898V}, current=Sensor {name=MGTA_current, value=0.128A}, power=Sensor {name=MGTA_power, value=0.109375W}},
 'MGTRA': Rail {name=MGTRA, voltage=Sensor {name=MGTRA_voltage, value=0.835V}, current=Sensor {name=MGTRA_current, value=0.039A}, power=Sensor {name=MGTRA_power, value=0.03125W}}}

As can be seen, the keys of the dictionary are the names of the voltage rails while the values are Rail objects which contain three sensors for the voltage, current and power. Due to limitations of the regulators used on the ZCU104 the resolution of most of the power railes are only to 1/8 W.

To see how power changes under CPU load we can use the DataRecorder class. For this example we are going to look at total board power as we load one of the CPU cores in Python.


In [2]:
recorder = pynq.DataRecorder(rails['12V'].power)

We can now use the recorder to monitor the applied sensor. For this example we'll sample the power every half second while sleeping and performing a dummy loop


In [3]:
import time
with recorder.record(0.5):
    time.sleep(10)
    for _ in range(10000000):
        pass
    time.sleep(10)

The DataRecorder exposes the sensor data as a pandas dataframe


In [4]:
recorder.frame


Out[4]:
Invocation 12V_power
2018-06-12 14:10:14.590922 0 14.300
2018-06-12 14:10:15.091951 0 13.825
2018-06-12 14:10:15.592879 0 13.825
2018-06-12 14:10:16.093752 0 13.825
2018-06-12 14:10:16.594644 0 13.800
2018-06-12 14:10:17.095583 0 13.825
2018-06-12 14:10:17.596424 0 13.825
2018-06-12 14:10:18.097266 0 13.825
2018-06-12 14:10:18.598133 0 13.800
2018-06-12 14:10:19.098978 0 13.800
2018-06-12 14:10:19.599862 0 13.825
2018-06-12 14:10:20.100796 0 13.825
2018-06-12 14:10:20.601771 0 13.800
2018-06-12 14:10:21.102624 0 13.800
2018-06-12 14:10:21.603606 0 13.825
2018-06-12 14:10:22.104582 0 13.825
2018-06-12 14:10:22.605515 0 13.825
2018-06-12 14:10:23.106495 0 13.800
2018-06-12 14:10:23.607339 0 13.800
2018-06-12 14:10:24.108314 0 13.825
2018-06-12 14:10:24.614368 0 13.875
2018-06-12 14:10:25.125480 0 13.800
2018-06-12 14:10:25.636581 0 13.975
2018-06-12 14:10:26.147517 0 14.000
2018-06-12 14:10:26.658481 0 14.025
2018-06-12 14:10:27.169514 0 14.000
2018-06-12 14:10:27.680505 0 14.000
2018-06-12 14:10:28.186375 0 13.800
2018-06-12 14:10:28.687186 0 13.800
2018-06-12 14:10:29.188076 0 13.800
2018-06-12 14:10:29.689050 0 13.825
2018-06-12 14:10:30.189987 0 13.800
2018-06-12 14:10:30.690962 0 13.800
2018-06-12 14:10:31.191853 0 13.800
2018-06-12 14:10:31.692828 0 13.825
2018-06-12 14:10:32.193813 0 13.825
2018-06-12 14:10:32.694828 0 13.825
2018-06-12 14:10:33.195804 0 13.800
2018-06-12 14:10:33.696651 0 13.825
2018-06-12 14:10:34.197641 0 13.825
2018-06-12 14:10:34.698540 0 13.825
2018-06-12 14:10:35.199527 0 13.825
2018-06-12 14:10:35.700457 0 13.825
2018-06-12 14:10:36.201440 0 13.825
2018-06-12 14:10:36.702443 0 13.800
2018-06-12 14:10:37.203427 0 13.800
2018-06-12 14:10:37.704407 0 13.800

or by plotting the results using matplotlib


In [5]:
%matplotlib inline
recorder.frame['12V_power'].plot()


Out[5]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f71ed0240>

We can get more information by using the mark function which will increment the invocation number without having to stop and start the recorder


In [6]:
recorder.reset()
with recorder.record(0.5):
    time.sleep(10)
    recorder.mark()
    for _ in range(10000000):
        pass
    recorder.mark()
    time.sleep(10)
    
recorder.frame.plot(subplots=True)


Out[6]:
array([<matplotlib.axes._subplots.AxesSubplot object at 0x7f728c9f60>,
       <matplotlib.axes._subplots.AxesSubplot object at 0x7f71de17f0>], dtype=object)

This clearly shows the power spike when the for loop starts running.