/*! \page page_mpm The Module Peripheral Manager (MPM) Architecture \tableofcontents For embedded USRP devices, there is an additional layer of control that UHD uses to access, configure, and control the hardware: The Module Peripheral Manager (MPM) daemon. In most cases, users of UHD do not need to know any details about MPM, but for some advanced use cases, it can be a useful tool. \section mpm_arch Architecture Overview Devices built using the MPM architecture must be capable of running an embedded Linux system. Within that system, the MPM daemon is continuously running and will allow access to device through a network-RPC based API. The UHD session calling into the device will thus be able to access that high-level API instead of directly having to interact with registers or other low-level components. This way, there is only a single driver in UHD for all MPM-powered devices. The source code for the MPM daemon is located in the `mpm/` subdirectory of the UHD repository (note that the UHD library's source code is located in the `host/` subdirectory). MPM is written in Python 3, C, and C++14. When starting a UHD session to control an MPM device, UHD will, under the hood, first try to access MPM over a network link to gain full control of the device. Once it has accessed MPM, it will send commands to MPM to start initializing the device for streaming usage. The MPM RPC connection is only used for command & control. Data is streamed over a separate connection, directly accessing the FPGA. This combines the flexibility of a network-RPC control with the high performance of direct streaming access. \section mpm_arch_claiming Device Claiming For most functionality, only a single UHD session can control MPM at the same time. To guarantee this, MPM implements a claiming mechanism. At the beginning of a UHD session, UHD will attempt to claim a device, which will either fail, or will succeed and create a \b token. This token can be used by the controlling UHD session to access peripherals which require exclusive access. UHD needs to re-claim the device within a certain interval to not lose access to the device. If UHD fails to claim a device, the assumption is made that the UHD process has terminated, and MPM will disallow further access until another, valid claim has been made. This is similar to the X3x0, which uses a claiming mechanism controlled by the ZPU. \section mpm_modes Network vs. Embedded Mode Devices such as the N310 allow running a UHD session both on the device, as well as off device. The former is called Embedded Mode, the latter Network Mode. In both cases, UHD will connect to MPM over a network socket. This allows the usage of the exact same driver for network and embedded modes, and thus, the same software can be compiled against UHD in both modes (keep in mind that the embedded systems are usually not as powerful as dedicated computers, so typically, high-performance applications will always run in network mode). \section mpm_debug Debugging Tools \subsection mpm_debug_python Python-based Debugging The Python API has additional methods for interacting with MPM. When creating a multi_usrp object, it is possible to get a direct reference to an MPM client: ~~~{.python} import uhd # Assume args contains valid device args: usrp = uhd.MultiUSRP(args) mpmc = usrp.get_mpm_client() # Now load device info over RPC and print: print(mpmc.get_device_info()) ~~~ The RPC API calls are directly mapped to Python. \b Note: When using the MPM client directly, it is possible to put UHD and the device into unsynchronized states. It is an advanced API, only recommended for unusual low-level access to the device, or debugging. \subsection mpm_debug_other MPM Tools The `mpm/tools/` subdirectory provides a useful tool for debugging and interacting with MPM called `mpm_shell.py`. It is a command line tool to directly access MPM commands. Like MPM, it requires Python 3. It can be invoked as follows: $ python3 tools/mpm_shell.py [-c] ni-n3xx-ABCD123 The `-c` switch will attempt to claim the device. The only other command line argument is a network address at which the device can be reached. If the connection succeeds, commands can be directly entered on the command line. MPM Shell supports tab completion and on-line help using the `?` operator: By prepending it to a command, its docstring will be displayed. > ?get_clock_source Arguments to the commands can simply be appended to the command itself: > set_clock_source external Arguments can also be Python expression, if the first character after the command itself is an `=` symbol: > set_db_eeprom =[0, open('eeprom.dat', 'w').read()] In this example, a list is created where the first element is a '0', and the second element is a string, read from a binary file. The list will be expanded to become the function arguments. In this case, the RPC call `set_db_eeprom` must thus have 2 arguments with the appropriate argument types. Note that MPM Shell can make use of non-standard RPC features, such as propagation of Python exceptions, which makes it a versatile debugging tool. \section mpm_shell_hijack Hijack Mode In hijack mode, MPM Shell is launched after another process, such as a UHD session, is already controlling the device. By passing the token to MPM Shell, it can access the device and perform low-level operations on the device. The token can be gained by scanning the log messages of the UHD session. It can be invoked as follows: $ python3 tools/mpm_shell.py -j $TOKEN ni-n3xx-ABCD123 */ // vim:ft=doxygen: