Writing Software

All the fuzz about drivers and interfaces is no use if there is no software being written!

Note

Ensure that you have properly activated the Python environment you created when installing XiBIF.

Start by navigating to sw/python. You will find that there are already some files in this directory.

  • regs.py: A class representing your register block

  • xibif_demo.py: A simple script testing the link speed of the AXI-stream

Note the line where the XibifConnection object is imported. This object serves as a hardware abstraction to interface with your board.

from xibif import XibifConnection
connection = XibifConnection(ip_address="192.168.1.10", verbose=False)
connection.open() # open the ports and connect to the FPGA

Register Access

Now there is a connection to the board and you can already read/write your registers. However, you would have to remember all the register addresses and bitfield masks. To avoid this, there is the generated register map which is used like this:

from regs import RegMap
handleReg = RegMap(connection)

handleReg.register_1 = 1            # set the value of register_1
handleReg.register_1_bf.field_1 = 1 # set the value of field_1 in register_1 (read-modify-write)
val = handleReg.register_2          # get the value of register_2
val = handleReg.register_2.field_1  # get the value of field_1 in register_2

Warning

Each access to a register property will trigger a read/write operation on the FPGA. If you have defined special register access policies such as rc (read to clear), reading a register might clear a field that you are not interested in.

Hint

If you want to modify multiple fields of the same register at one time, you will have to use standard masking and bit-shifting. The appropriate values are also saved in the RegMap. For each register a <REGNAME>_ADDR property specifies the register’s address, and for each field the <REGNAME>_<FIELDNAME>_MSK and <REGNAME>_<FIELDNAME>_POS provide the values for bitfield operations.

A field is read like this:

rdata       = connection.read(handleReg.<REGNAME>_ADDR)
field_value = (rdata >> handleReg.<REGNAME>_<FIELDNAME>_POS) & handleReg.<REGNAME>_<FIELDNAME>_MSK

and written like this:

valToWrite = 1
rdata = connection.read(handleReg.<REGNAME>_ADDR)
rdata = rdata & (~(handleReg.<REGNAME>_<FIELDNAME>_MSK << handleReg.<REGNAME>_<FIELDNAME>_POS))
rdata = rdata | (valToWrite << handleReg.<REGNAME>_<FIELDNAME>_POS)
connection.write(handleReg.<REGNAME>_ADDR, rdata)

You can perform multiple field modifications before calling xibif.write().

Streaming

In order to write to the AXI-stream, use:

connection.write_stream(streamToWrite)

To read the stream, you must specify how many bytes of data you would like to receive. Typically, one wants to read the entire content of the FIFO. Use:

connection.read_status()
stream = connection.read_stream(connection.fifoPl2PcFill)

There is a special function, that allows to read all the data in the FIFO without specifying the size. This function is usually faster than the previous one:

stream = connection.read_stream_all()

It might be possible that an error occurs during streaming. To check if an error has occurred, the properties connection.errorPc2Pl and connection.errorPl2Pc can be used. The error flags are saved until a flush of the FIFOs is performed. To flush the FIFOs, use:

connection.flush_stream()

The following error codes are possible:

Error Value

Description

0

No error occurred during streaming.

1

An AXI-Error on the AXI-Master interface occurred during streaming.

2

The FIFO overflowed during streaming. This can happen if the PC or PL is not fast enough to read the data.

3

Both an AXI-Error and a FIFO overflow occurred during streaming.

AXI-Access

The XiBIF platform provides a simple interface to access the AXI bus. This is useful for accessing IP blocks that are not part of the XiBIF platform. The interface is similar to the register access, but you have to specify the address of the register you want to access.

To read a register, use:

connection.read_axi(address)

Similarly, to write to a register, use:

connection.write_axi(address, value, mask=0xFFFFFFFF)

The mask is optional and defaults to 0xFFFFFFFF. The mask is used to specify which bits of the register you want to write. For example, if you want to write only to the lower 16 bits of a register, you can use:

connection.write_axi(address, value, 0x0000FFFF)

Error Codes

Only AXI-Addresses that are routed through the AXI Firewall IP are accessible. For Zynq-7000 platforms, the address needs to be in the range of 0x40000000 to 0x4FFFFFFF. For ZynqMP platforms, the address needs to be in the range of 0xA0000000 to 0xAFFFFFFF. Accessing other addresses will result in an error. It is further not possible to access the XiBIF IP Blocks with these functions. Calls to these addresses will result in an error.The following error codes are possible:

Error Code

Description

AXI_ACCESS_OK

Access was successful.

AXI_ACCESS_NOT_ALLOWED_RANGE

The address is not within the allowed range. It must be in the range of 0x40000000 to 0x4FFFFFFF for Zynq-7000 platforms or 0xA0000000 to 0xAFFFFFFF for ZynqMP platforms.

AXI_ACCESS_NOT_ALLOWED_XIBIF_RANGE

The address is within the XiBIF reserved range and cannot be accessed.

AXI_FIREWALL_ERROR

The AXI Firewall detected an error.

AXI_FIREWALL_DATA_DEC0DEE3_DEADFA11

The AXI Firewall returned a specific error code indicating a data issue. If you expect to read the value 0xDEADC0DE or 0xDEADFA11 you can disable this error by setting the property disable_axi_check_DEC0DEE3_DEADFA11 to True.

AXI_ACCESS_ADDRESS_NOT_ALIGNED

The address is not aligned to 32bit values. The address must be a multiple of 4.

Timeout

The timeout of the AXI Firewall IP can be configured using the command xibif settings. It is specified in clock cycles of the AXI clock. The default is 100 clock cycles.

xibif settings --hw.timeout_axi_firewall_clocks 100

Debugging

UART

You can enable debugging information to be printed over UART. The simplest is to print out the status information of a connected XiBIF device by calling:

connection.print_status_uart()

Alternatively, you can enable all DEBUG_PRINTF in the software source code by calling:

connection.set_debug_uart(True)

Be aware that this will result in a lot of information, as every read, write, violation and so forth will be printed.

To disable the debug information again, call the function with False as an argument.

connection.set_debug_uart(False)

Interactive Shell

You can connect to a XiBIF board using an interactive shell. This lets you easily set and get registers and evaluate the AXI-Stream. Assuming you are using the default setup, type xibif shell and you should connect to the board automatically. Use help -v once the shell is open to find out which commands you can use.