Introduction to the Register Map

It is possible to access the register map for IP in the overlay directly. This functionality requires that the .hwh file is distributed along with the bitstream. This can be generated from Vivado.

In this example we are going us the GPIO blocks controlling the buttons and LEDs to demonstrate how to explore and interact with the register map.

First we need to import the base overlay


In [1]:
from pynq.overlays.base import BaseOverlay

base = BaseOverlay('base.bit')


Next we get the drivers for


In [2]:
btns = base.btns_gpio
leds = base.leds_gpio

We can now print the register map and the current values of the registers by printing the representation of the .register_map.


In [3]:
btns.register_map


Out[3]:
RegisterMap {
  GPIO_DATA = Register(Channel_1_GPIO_DATA=0),
  GPIO_TRI = Register(Channel_1_GPIO_TRI=15),
  GPIO2_DATA = Register(Channel_2_GPIO_DATA=0),
  GPIO2_TRI = Register(Channel_2_GPIO_TRI=4294967295),
  GIER = Register(Global_Interrupt_Enable=1),
  IP_IER = Register(Channel_1_Interrupt_Enable=0, Channel_2_Interrupt_Enable=0),
  IP_ISR = Register(Channel_1_Interrupt_Status=0, Channel_2_Interrupt_Status=0)
}

To access values programmatically we can walk the object model


In [4]:
btns.register_map.GPIO_DATA.Channel_1_GPIO_DATA


Out[4]:
0

If there any more information on a register is available it can be accessed using help. This will list any description of the register available as well as the subfields. Here we see that the IP_IER register the interrupt enable register and contains two fields - one for each channel.


In [5]:
help(btns.register_map.IP_IER)


Help on RegisterIP_IER in module pynq.register_map object:

class RegisterIP_IER(Register)
 |  IP Interrupt Enable register
 |  
 |  Method resolution order:
 |      RegisterIP_IER
 |      Register
 |      builtins.object
 |  
 |  Data descriptors defined here:
 |  
 |  Channel_1_Interrupt_Enable
 |      IP Interrupt Enable register
 |  
 |  Channel_2_Interrupt_Enable
 |      IP Interrupt Enable register
 |  
 |  ----------------------------------------------------------------------
 |  Data and other attributes defined here:
 |  
 |  _fields = {'Channel_1_Interrupt_Enable': {'access': 'read-write', 'bit...
 |  
 |  ----------------------------------------------------------------------
 |  Methods inherited from Register:
 |  
 |  __getitem__(self, index)
 |      Get the register value.
 |      
 |      This method accepts both integer index, or slice as input parameters.
 |      
 |      Parameters
 |      ----------
 |      index : int | slice
 |          The integer index, or slice to access the register value.
 |  
 |  __index__(self)
 |      Return an index containing the value of the register
 |  
 |  __init__(self, address, width=32, debug=False, buffer=None)
 |      Instantiate a register object.
 |      
 |      Parameters
 |      ----------
 |      address : int
 |          The address of the register.
 |      width : int
 |          The width of the register, e.g., 32 (default) or 64.
 |      debug : bool
 |          Turn on debug mode if True; default is False.
 |      buffer : Buffer
 |          Buffer object to use for reading and writing the value
 |          of the register. If None the address is assumed to be
 |          an absolute physical address
 |  
 |  __int__(self)
 |      Return an integer of the value of the register
 |  
 |  __repr__(self)
 |      Print a representation of the Register and all its fields
 |      
 |      If the Register has been subclassed with fields then these
 |      will be printed otherwise the return string will contain the
 |      value of the entire register
 |  
 |  __setitem__(self, index, value)
 |      Set the register value.
 |      
 |      This method accepts both integer index, or slice as input parameters.
 |      
 |      Parameters
 |      ----------
 |      index : int | slice
 |          The integer index, or slice to access the register value.
 |  
 |  __str__(self)
 |      Print the register value.
 |      
 |      This method is overloaded to print the register value. The output
 |      is a string in hex format.
 |  
 |  _debug(self, s, *args)
 |      The method provides debug capabilities for this class.
 |      
 |      Parameters
 |      ----------
 |      s : str
 |          The debug information format string
 |      *args : any
 |          The arguments to be formatted
 |      Returns
 |      -------
 |      None
 |  
 |  _reordered_setitem(self, value, index)
 |      Wrapped version of __setitem__ for better use with
 |      functools.partial
 |  
 |  ----------------------------------------------------------------------
 |  Class methods inherited from Register:
 |  
 |  count(index, width=32) from builtins.type
 |      Provide the number of bits accessed by an index or slice
 |      
 |      This method accepts both integer index, or slice as input parameters.
 |      
 |      Parameters
 |      ----------
 |      index : int | slice
 |          The integer index, or slice to access the register value.
 |      width : int
 |          The number of bits accessed.
 |  
 |  create_subclass(name, fields, doc=None) from builtins.type
 |      Create a subclass of Register that has properties for the
 |      specified fields
 |      
 |      Parameters
 |      ----------
 |      name : str
 |          A suffix for the name of the subclass
 |      fields : dict
 |          A Dictionary containing the fields to add to the subclass
 |      
 |      The fields should be in the form used by the ip_dict, namely
 |      name -> {
 |          access = "read-only" | "read-write" | "write-only",
 |          bit_offset: int, bit_width: int, description: str
 |      }
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors inherited from Register:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)

Writing can be performed either to registers or fields. Note that register slice access are performed using the RTL conventions of hi:lo inclusive to match the format commonly seen in data sheets rather than the lo:hi inclusive-exclusive more common in Python ranges.


In [6]:
leds.register_map.GPIO_DATA.Channel_1_GPIO_DATA = 8
leds.register_map.GPIO_DATA[2:0] = 5

At present this functionality is limited to scalar registers in IP that has the attached metadata. This includes most IP in the Xilinx IP catelog and any HLS-generated IP that uses AXI-lite for control registers.