The PYNQ Microblaze infrastructure is built on top of a remote procedure call (RPC) layer which is responsible for forwarding function calls from the Python environment to the Microblaze and handling all data transfer.
The RPC layer supports a subset of the C programming language for interface functions, although any functions may be used internally within the Microblaze program. Any function which does not conform to these requirements will be ignored. The limitations are:
- No
struct
orunion
for either parameters or return types. - No returning of pointer types
- No pointers to pointers
All return values are passed back to Python through copying. The transfer of function arguments depends on the type used. For a given non-void primitive the following semantics:
- Non-pointer types are copied from PYNQ to the microblaze
- Const pointer types are copied from Python to the Microblaze
- Non-const pointer types are copied from Python to the Microblaze and then copied back after completion of the function.
The timeline of the execution of the function can be seen below:
The Python struct
module is used to convert the Python type passed to the function into the appropriately sized integer or floating point value for the Microblaze. Out of range values will result in an exception being raised and the Microblaze function not running. Arrays of types are treated similarly with the struct
module used to perform the conversion from an array of Python types to the C array. For non-const arrays, the array is updated in place so that the return values are available to the caller. The only exception to these conversion rules are char
and const char
pointers which are optimised for Python bytearray
and bytes
types. Note that calling a function with a non-const char*
argument with a bytes
object will result in an error because bytes
objects are read-only. This will caught prior to the Microblaze function being called.
For non-void return functions, the Python functions are synchronous and will wait for the C function to finish prior to returning to the caller. For functions that return void then the function is called asynchronously and the Python function will return immediately. This entails long-running, independent functions to run on the Microblaze without blocking the Python thread. While the function is running, no other functions can be called unless the long-running process frequently calls yield
(from yield.h
) to allow the RPC runtime to service requests. Please note - there is no multi-threading available inside the Microblaze so attempting to run two long-running processes simultaneously will result in only one executing regardless of the use of yield
.
The RPC engine fully supports typedefs and provides an additional mechanism to allow for C functions to appear more like Python classes. The RPC layer recognises the idiom where the name of a typedef is used as the prefix for a set of function names. Taking an example from the PYNQ Microblaze library, the i2c
typedef has corresponding functions i2c_read
and i2c_write
which take an i2c
type as the first parameter. With this idiom the RPC creates a new class called i2c
which has read
and write
methods. Any C functions returning an i2c
typedef now return an instance of this class. For this conversion to be done, the following three properties must hold:
- The typedef is of a primitive type
- There is at least one function returning the typedef
- There is at least one function named according to the pattern