<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Programming Microblaze Subsystems from Jupyter
In the Base I/O overlays that accompany the PYNQ release Microblazes are used to control peripherals attached to the various connectors. These can either be programmed with existing programs compiled externally or from within Jupyter. This notebook explains how the Microblazes can be integrated into Jupyter and Python.
The Microblaze is programmed in C as the limited RAM available (64 KB) limits what runtimes can be loaded - as an example, the MicroPython runtime requires 256 KB of code and data space. The PYNQ framework provides a mechanism to write the C code inside Jupyter, compile it, load it on to the Microblaze and then execute and interact with it.
The first stage is to load an overlay.
Step1:  Now we can write some C code. The %%microblaze magic provides an environment where we can write the code and it takes a single argument - the Microblaze we wish to target this code at. This first example simply adds two numbers together and returns the result.
Step2:  The functions we defined in the magic are now available for us to interact with in Python as any other function.
Step3:  Data Motion
The main purpose of the Python bindings it to transfer data between the host and slave processors. For simple cases, any primitive C type can be used as function parameters and return values and Python values will be automatically converted as necessary.
Step4:  Arrays can be passed in two different way. If a type other than void is provided then the data will be copied to the microblaze and if non-const the data will be copied back as well. And iterable and modifiable object can be used as the argument in this case.
Step5:  Finally we can pass a void pointer which will allow the Microblaze to directly access the memory of the host processing system for transferring large quantities of data. In Python these blocks of memory should be allocated using the pynq.allocate function and it is the responsibility of the programmer to make sure that the Python and C code agree on the types used.
Step6:  Debug printing
One unique feature of the PYNQ Microblaze environment is the ability to print debug information directly on to the Jupyter or Python console using the new pyprintf function. This functions acts like printf and format in Python and allows for a format string and variables to be passed back to Python for printing. In this release on the %d format specifier is supported but this will increase over time.
To use pyprintf first the appropriate header needs to be included
Step7:  Long running processes
So far all of the examples presented have been synchronous with the Python code with the Python code blocking until a result is available. Some applications call instead for a long-running process which is periodically queried by other functions. If a C function return void then the Python process will resume immediately leaving the function running on its own.
Other functions can be run while the long-running process is active but as there is no pre-emptive multithreading the persistent process will have to yield at non-timing critical points to allow other queued functions to run.
In this example we launch a simple counter process and then pull the value using a second function.
Step8:  We can now start the counter going.
Step9:  And interrogate its current value
Step10:  There are some limitations with using pyprintf inside a persistent function in that the output will not be displayed until a subsequent function is called. If the buffer fills in the meantime this can cause the process to deadlock.
Only one persistent process can be called at once - if another is started it will block the first until it returns. If two many processes are stacked in this way a stack overflow may occur leading to undefined results.
Creating class-like objects
In the C code typedefs can be used to create psuedo classes in Python. If you have a typedef called my_class then any functions that being my_class_ are assumed to be associated with it. If one of those functions takes my_class as the first argument it is taken to be equivalent to self. Note that the typedef can only ultimately refer a primitive type. The following example does some basic modular arithmetic base 53 using this idiom.
Step11:  We can now create instances using our create function and call the add method on the returned object. The underlying value of the typedef instance can be retrieved from the .val attribute.
<ASSISTANT_TASK:>
Python Code:
from pynq.overlays.base import BaseOverlay
base = BaseOverlay('base.bit')
Explanation: Programming Microblaze Subsystems from Jupyter
In the Base I/O overlays that accompany the PYNQ release Microblazes are used to control peripherals attached to the various connectors. These can either be programmed with existing programs compiled externally or from within Jupyter. This notebook explains how the Microblazes can be integrated into Jupyter and Python.
The Microblaze is programmed in C as the limited RAM available (64 KB) limits what runtimes can be loaded - as an example, the MicroPython runtime requires 256 KB of code and data space. The PYNQ framework provides a mechanism to write the C code inside Jupyter, compile it, load it on to the Microblaze and then execute and interact with it.
The first stage is to load an overlay.
End of explanation
%%microblaze base.PMODA
int add(int a, int b) {
    return a + b;
}
Explanation: Now we can write some C code. The %%microblaze magic provides an environment where we can write the code and it takes a single argument - the Microblaze we wish to target this code at. This first example simply adds two numbers together and returns the result.
End of explanation
add(4,6)
Explanation: The functions we defined in the magic are now available for us to interact with in Python as any other function.
End of explanation
%%microblaze base.PMODA
float arg_passing(float a, char b, unsigned int c) {
    return a + b + c;
}
arg_passing(1, 2, 3)
Explanation: Data Motion
The main purpose of the Python bindings it to transfer data between the host and slave processors. For simple cases, any primitive C type can be used as function parameters and return values and Python values will be automatically converted as necessary.
End of explanation
%%microblaze base.PMODA
int culm_sum(int* val, int len) {
    int sum = 0;
    for (int i = 0; i < len; ++i) {
        sum += val[i];
        val[i] = sum;
    }
    return sum;
}
numbers = [i for i in range(10)]
culm_sum(numbers, len(numbers))
print(numbers)
Explanation: Arrays can be passed in two different way. If a type other than void is provided then the data will be copied to the microblaze and if non-const the data will be copied back as well. And iterable and modifiable object can be used as the argument in this case.
End of explanation
%%microblaze base.PMODA
long long big_sum(void* data, int len) {
    int* int_data = (int*)data;
    long long sum = 0;
    for (int i = 0; i < len; ++i) {
        sum += int_data[i];
    }
    return sum;
}
from pynq import allocate
buffer = allocate(shape=(1024 * 1024), dtype='i4')
buffer[:] = range(1024*1024)
big_sum(buffer, len(buffer))
Explanation: Finally we can pass a void pointer which will allow the Microblaze to directly access the memory of the host processing system for transferring large quantities of data. In Python these blocks of memory should be allocated using the pynq.allocate function and it is the responsibility of the programmer to make sure that the Python and C code agree on the types used.
End of explanation
%%microblaze base.PMODA
#include <pyprintf.h>
int debug_sum(int a, int b) {
    int sum = a + b;
    pyprintf("Adding %d and %d to get %d\n", a, b, sum);
    return sum;
}
debug_sum(1,2)
Explanation: Debug printing
One unique feature of the PYNQ Microblaze environment is the ability to print debug information directly on to the Jupyter or Python console using the new pyprintf function. This functions acts like printf and format in Python and allows for a format string and variables to be passed back to Python for printing. In this release on the %d format specifier is supported but this will increase over time.
To use pyprintf first the appropriate header needs to be included
End of explanation
%%microblaze base.PMODA
#include <yield.h>
static int counter = 0;
void start_counter() {
    while (1) {
        ++counter;
        yield();
    }
}
int counter_value() {
    return counter;
}
Explanation: Long running processes
So far all of the examples presented have been synchronous with the Python code with the Python code blocking until a result is available. Some applications call instead for a long-running process which is periodically queried by other functions. If a C function return void then the Python process will resume immediately leaving the function running on its own.
Other functions can be run while the long-running process is active but as there is no pre-emptive multithreading the persistent process will have to yield at non-timing critical points to allow other queued functions to run.
In this example we launch a simple counter process and then pull the value using a second function.
End of explanation
start_counter()
Explanation: We can now start the counter going.
End of explanation
counter_value()
Explanation: And interrogate its current value
End of explanation
%%microblaze base.PMODA
typedef unsigned int mod_int;
mod_int mod_int_create(int val) { return val % 53; }
mod_int mod_int_add(mod_int lhs, int rhs) { return (lhs + rhs) % 53; }
Explanation: There are some limitations with using pyprintf inside a persistent function in that the output will not be displayed until a subsequent function is called. If the buffer fills in the meantime this can cause the process to deadlock.
Only one persistent process can be called at once - if another is started it will block the first until it returns. If two many processes are stacked in this way a stack overflow may occur leading to undefined results.
Creating class-like objects
In the C code typedefs can be used to create psuedo classes in Python. If you have a typedef called my_class then any functions that being my_class_ are assumed to be associated with it. If one of those functions takes my_class as the first argument it is taken to be equivalent to self. Note that the typedef can only ultimately refer a primitive type. The following example does some basic modular arithmetic base 53 using this idiom.
End of explanation
a = mod_int_create(63)
b = a.add(4)
print(b)
print(b.val)
Explanation: We can now create instances using our create function and call the add method on the returned object. The underlying value of the typedef instance can be retrieved from the .val attribute.
End of explanation
<END_TASK>