top of page

MicroZed Chronicles: Sysmon and I2C access

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.

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.

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.

The JTAG DRP frame we need to send in looks as below

With the commands as below

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.

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.

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

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



Boards

Get an Adiuvo 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.


0 comments

Comments


bottom of page