XADC as part of a temperature reporting system which allowed me to demonstrate the power of using a model based approach.
One of the great things about the XADC and Sysmon is that we care able to use them in our designs to not only remove the need for external ADCs but also to allow us to monitor the devices power supply rails and temperature. If we so desire we can then take action within our FPGA / SoC design should something begin to drift, ideal capabilities for safety critical and secure designs.
When using UltraScale and UltraScale+ devices we are able to access the Sysmon using an I2C interface. This I2C interface enables a remote system controller to be able to monitor the status of the target device. This can be monitored both pre configuration and post configuration thanks to dedicated pins on the package. This enables the remote system controller, normally a smaller FPGA or Microcontroller on the same board as the complex device to be able to sequence the power supplies and confirm the power rails are as expected before enabling configuration of the complex device to begin. The I2C pins can also be routed to any GPIO pins are configuration.
Accessing the Sysmon over I2C is straight forward, for this blog I am going to demonstrate how we are able to access the Sysmon externally using a simple I2CDriver test equipment. Using this device enables me to create a set of simple python scripts which can be used to read the temperatures and voltages on the board.
For this project I will be using a ZCU106 as the target board and routing the I2C pins to the PMOD header.
To get started I created a project for the ZCU106 and instantiated a Sysmon component selecting an I2C interface.
![](https://static.wixstatic.com/media/ad48ed_29f7ff5c741b40e4acd0c9c050e36af6~mv2.png/v1/fill/w_980,h_658,al_c,q_90,usm_0.66_1.00_0.01,enc_auto/ad48ed_29f7ff5c741b40e4acd0c9c050e36af6~mv2.png)
Providing the clock for the Sysmon is the onboard oscillator and a clocking wizard generates the 100MHz reference clock. I also added in a ILA and VIO to ensure I could monitor and control the Sysmon during my development.
![](https://static.wixstatic.com/media/ad48ed_90a4a44a479b4eaeb46b38734e5ceee4~mv2.png/v1/fill/w_980,h_448,al_c,q_90,usm_0.66_1.00_0.01,enc_auto/ad48ed_90a4a44a479b4eaeb46b38734e5ceee4~mv2.png)
With the design build and bit stream available I was able to download it to the ZCU106 and get started with the development of the python SW.
I decided to use python as it then clearly shows the steps we need to communicate with the Sysmon using I2C. We can then convert this to C etc for our embedded microcontroller e.g. MicroBlaze V or FPGA state machine.
To understand how we read and write the Sysmon over I2C we can find full instructions in UG580.
However, using I2C we access the Sysmon in the same way as we would using the JTAG Dynamic Reconfiguration port (DRP). This means can access the register interfaces shown below.
![](https://static.wixstatic.com/media/ad48ed_5163becc6b3647848322233138a352ab~mv2.png/v1/fill/w_698,h_491,al_c,q_85,enc_auto/ad48ed_5163becc6b3647848322233138a352ab~mv2.png)
The JTAG DRP frame we need to send in looks as below
![](https://static.wixstatic.com/media/ad48ed_a94e4b9b07524a2fbe7b084f9be0951e~mv2.png/v1/fill/w_666,h_119,al_c,q_85,enc_auto/ad48ed_a94e4b9b07524a2fbe7b084f9be0951e~mv2.png)
With the commands as below
![](https://static.wixstatic.com/media/ad48ed_9521ef77b4dd498bab7027ae0fe8bdb5~mv2.png/v1/fill/w_376,h_142,al_c,q_85,enc_auto/ad48ed_9521ef77b4dd498bab7027ae0fe8bdb5~mv2.png)
However we need to format it for I2C transaction, when reading the Sysmon registers we still need to send in the 32 bit command above using a I2C write and then issue an I2C write.
The write and read waveforms are presented below.
![](https://static.wixstatic.com/media/ad48ed_2c6fe702d8d544a29e77be0485c80e33~mv2.png/v1/fill/w_780,h_163,al_c,q_85,enc_auto/ad48ed_2c6fe702d8d544a29e77be0485c80e33~mv2.png)
![](https://static.wixstatic.com/media/ad48ed_2705f6564a8342e78b760011682ac691~mv2.png/v1/fill/w_772,h_119,al_c,q_85,enc_auto/ad48ed_2705f6564a8342e78b760011682ac691~mv2.png)
For the I2C transaction we need to transmit the lower bytes of the words first.
I created a simple python program which enables the temperature and voltages to be read back using a I2CDriver.
![](https://static.wixstatic.com/media/ad48ed_bb953aa0236f41a098c1e31ad39b667d~mv2.jpg/v1/fill/w_980,h_1742,al_c,q_85,usm_0.66_1.00_0.01,enc_auto/ad48ed_bb953aa0236f41a098c1e31ad39b667d~mv2.jpg)
The python has several functions, one which gets the temperature and another which gets a specified voltage identified by providing the register address. There are other functions which form the packet and flip the bytes to enable easier transfer over I2C.
import i2cdriver
def convert_reg_to_temp(reg_val):
temp = ((reg_val * 509.3140064) / (2 ** 16)) - 280.23087870
return temp
def swap_bytes(byte1, byte2):
return (byte2 << 8) | byte1
def get_temp():
i2c = i2cdriver.I2CDriver("COM18")
i2c.start(0x32,0)
i2c.write([0x00,0x00,0x00,0x04])
i2c.stop()
i2c.start(0x32,1)
data = i2c.read(2)
i2c.stop()
data = swap_bytes(data[0],data[1])
temp = convert_reg_to_temp(data)
return temp
def flip_endian(vector):
vector &= 0xFFFFFFFF
byte0 = (vector & 0xFF000000) >> 24
byte1 = (vector & 0x00FF0000) >> 16
byte2 = (vector & 0x0000FF00) >> 8
byte3 = (vector & 0x000000FF)
return [byte3, byte2, byte1, byte0]
def get_volt(bytes):
i2c = i2cdriver.I2CDriver("COM18")
i2c.start(0x32,0)
i2c.write([bytes[0],bytes[1],bytes[2],bytes[3]])
i2c.stop()
i2c.start(0x32,1)
data = i2c.read(2)
i2c.stop()
data = swap_bytes(data[0],data[1])
data = (data /65536 ) *3.0
return data
def create_structure(address):
address &= 0xFF
command = 0x01
data = 0x0000
result = (command << 26) | (address << 16) | data
result = result & 0xFFFFFFFF
flipped = flip_endian(result)
return flipped
temp = get_temp()
print("current device temperature is", temp, "C")
struct = create_structure(0x01)
data = get_volt(struct)
print("current voltage is", data, "V")
Running this on the ZCU106 enables me to read the temperatures and voltages using the I2CDriver.
We can see in running the script twice the first time temperature is 28.3C and VccBRAM is 0.89V while in the second reading the temperature is 27.8C and Vccint is 0.85V
![](https://static.wixstatic.com/media/ad48ed_73d13d8f3f54478299fcba5970262095~mv2.png/v1/fill/w_498,h_195,al_c,q_85,enc_auto/ad48ed_73d13d8f3f54478299fcba5970262095~mv2.png)
Now we can use what we have learned as we develop our more complex solutions with more complex devices which need external supervision and sequencing.
Workshops and Webinars
If you enjoyed the blog why not take a look at the free webinars, workshops and training courses we have created over the years. Highlights include
Professional PYNQ Learn how to use PYNQ in your developments
Introduction to Vivado learn how to use AMD Vivado
Ultra96, MiniZed & ZU1 three day course looking at HW, SW and PetaLinux
Arty Z7-20 Class looking at HW, SW and PetaLinux
Mastering MicroBlaze learn how to create MicroBlaze solutions
HLS Hero Workshop learn how to create High Level Synthesis based solutions
Perfecting Petalinux learn how to create and work with PetaLinux OS
Boards
Get an Adiuvo development board
Adiuvo Spartan 7 / RPi 2040 Embedded System Development Board
Embedded System Book
Do you want to know more about designing embedded systems from scratch? Check out our book on creating embedded systems. This book will walk you through all the stages of requirements, architecture, component selection, schematics, layout, and FPGA / software design. We designed and manufactured the board at the heart of the book! The schematics and layout are available in Altium here Learn more about the board (see previous blogs on Bring up, DDR validation, USB, Sensors) and view the schematics here.
Comments