{ "cells": [ { "attachments": {}, "cell_type": "markdown", "id": "3100fc3f", "metadata": {}, "source": [ "# 🐝🔌 Measuring power consumption on XyloℱAudio HDK" ] }, { "cell_type": "markdown", "id": "731c22c0", "metadata": {}, "source": [ "> **_Note:_** In this tutorial XyloℱAudio refers to XyloℱAudio 2 and XyloℱAudio 3. In case differences occur we will explicitly name the correct HDK.\n", "\n", "> **_Note 2:_** This tutorial assume you have already build and mapped a network to XyloℱAudio. If this is not the case, please follow the tutorial at :ref:`/devices/quick-xylo/xylo-audio-intro.ipynb`" ] }, { "cell_type": "raw", "id": "d7b886aa", "metadata": { "raw_mimetype": "text/restructuredtext", "vscode": { "languageId": "raw" } }, "source": [ ":py:class:`.syns61201.XyloSamna` and :py:class:`.syns65302.XyloSamna` (respectively for XyloAudio 2 and XyloAudio 3) provide an interface to real-time power measurements on the XyloAudio HDK.\n", "Current on several power nets on the chip can be sampled asynchronously while the device is in operation.\n", "\n", "The :py:meth:`~.XyloSamna.evolve` method provides a power measurement interface while the device is in inference mode, and the :py:class:`.XyloSamna` module can also be used to measure idle power." ] }, { "cell_type": "code", "execution_count": null, "id": "d532d258", "metadata": {}, "outputs": [], "source": [ "# Specify the xylo board you would like to use for this tutorial\n", "# Available boards: XyloAudio2 and XyloAudio3\n", "xylo_board_name = 'XyloAudio3'\n", "\n", "# - Imports dependent on your HDK\n", "# - XyloAudio 2\n", "if xylo_board_name == 'XyloAudio2':\n", " import rockpool.devices.xylo.syns61201 as xa\n", " from rockpool.devices.xylo.syns61201 import xa2_devkit_utils as xa_utils\n", "# - XyloAudio 3\n", "elif xylo_board_name == 'XyloAudio3':\n", " import rockpool.devices.xylo.syns65302 as xa\n", " from rockpool.devices.xylo.syns65302 import xa3_devkit_utils as xa_utils\n", "\n", "# Available boards: XyloAudio2 and XyloAudio3\n", "xylo_board_name = 'XyloAudio3'\n", "\n", "\n", "import numpy as np" ] }, { "cell_type": "code", "execution_count": 2, "id": "ed018313", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The connected Xylo HDK contains a XyloAudio 3. Importing `rockpool.devices.xylo.syns65302`\n", "[]\n" ] } ], "source": [ "# - Find and connect to a XyloAudio HDK\n", "from rockpool.devices.xylo import find_xylo_hdks\n", "xylo_hdk_nodes, modules, versions = find_xylo_hdks()\n", "print(xylo_hdk_nodes)\n", "\n", "hdk = None\n", "\n", "for version, xylo in zip(versions, xylo_hdk_nodes):\n", " if version == \"syns61201\":\n", " hdk = xylo\n", " # - For XyloAudio 3\n", " elif version == \"syns65302\":\n", " hdk = xylo\n", "\n", "if hdk is None:\n", " assert False, 'This tutorial requires a connected XyloAudio HDK to demonstrate.'\n", "\n", "\n" ] }, { "cell_type": "raw", "id": "b231ff04", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ "On instantiation, :py:class:`.XyloSamna` can specify a power sampling frequency.\n", "By default, power is measured at 5 Hz." ] }, { "cell_type": "code", "execution_count": 3, "id": "f5b61064", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "WARNING:samna.xyloAudio3:Requested PDM clock frequency 195312 could not be achieved, used 194552.52918287937 instead\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "XyloSamna with shape (2, 8, 2)\n" ] } ], "source": [ "# - Define the size of the network layers\n", "Nin = 2\n", "Nhidden = 4\n", "Nout = 2\n", "dt = 1e-3\n", "\n", "# load config created in XyloAudio intro tutorial\n", "%store -r config\n", "\n", "# - For XyloAudio 2\n", "if xylo_board_name == 'XyloAudio2':\n", " # - Set a low clock frequency for the XyloAudio 2 device\n", " xa_utils.set_xylo_core_clock_freq(hdk, 6.25)\n", " # - Use XyloSamna to deploy to the HDK\n", " modSamna = xa.XyloSamna(hdk, config, dt = 10e-3, power_frequency=20.)\n", "# - For XyloAudio 3\n", "elif xylo_board_name == 'XyloAudio3':\n", " import samna\n", " # - Set a low clock frequency for the XyloAudio 3 device\n", " xa_utils.set_xylo_core_clock_freq(hdk, 6.25)\n", " # - Use XyloSamna to deploy to the HDK\n", " modSamna = xa.XyloSamna(hdk, config, dt = 10e-3, power_frequency=20.)\n", " \n", "print(modSamna)" ] }, { "cell_type": "code", "execution_count": 4, "id": "80ced3e2", "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'io_power': array([4.46644719e-05, 5.27330065e-05, 5.42548444e-05, 5.26624547e-05,\n", " 5.22731397e-05, 5.24438111e-05, 5.15508297e-05, 5.20056725e-05,\n", " 5.31936278e-05, 5.20105327e-05, 5.31826708e-05, 5.24881628e-05,\n", " 5.22707500e-05, 5.29561894e-05, 5.17823424e-05, 5.23655495e-05,\n", " 5.30992342e-05, 5.22785888e-05, 5.24720042e-05, 5.34786841e-05,\n", " 5.24374957e-05, 5.29416290e-05, 5.25395594e-05, 5.25345340e-05,\n", " 5.27847456e-05, 5.19925713e-05, 5.27561207e-05, 5.26861072e-05,\n", " 5.25215358e-05, 5.23542377e-05, 5.24205504e-05, 5.20894094e-05,\n", " 5.27775252e-05, 5.25651484e-05, 5.22221154e-05, 5.28711553e-05,\n", " 5.25564873e-05, 5.21012408e-05, 5.30044813e-05, 5.28361825e-05,\n", " 5.13447330e-05, 5.22164881e-05]), 'analog_power': array([1.17465585e-05, 1.19348250e-05, 1.23008853e-05, 1.15634605e-05,\n", " 1.31435621e-05, 1.27060542e-05, 1.15766181e-05, 1.26167961e-05,\n", " 1.23855745e-05, 1.26804249e-05, 1.25783420e-05, 1.18251313e-05,\n", " 1.24061831e-05, 1.18949793e-05, 1.26562390e-05, 1.15477583e-05,\n", " 1.25863335e-05, 1.19010734e-05, 1.22843344e-05, 1.17492459e-05,\n", " 1.22880637e-05, 1.15174232e-05, 1.24418151e-05, 1.20264690e-05,\n", " 1.22098524e-05, 1.27235501e-05, 1.21457320e-05, 1.19670520e-05,\n", " 1.16380779e-05, 1.24937181e-05, 1.17544295e-05, 1.27169458e-05,\n", " 1.25136790e-05, 1.23984491e-05, 1.19406408e-05, 1.31072229e-05,\n", " 1.21830276e-05, 1.27201528e-05, 1.20502382e-05, 1.25654505e-05,\n", " 1.15136281e-05, 1.19651731e-05]), 'digital_power': array([0.00078813, 0.00058126, 0.00058306, 0.00058354, 0.00058494,\n", " 0.00058481, 0.00058357, 0.00058395, 0.00058351, 0.00058484,\n", " 0.0005854 , 0.00058323, 0.0005837 , 0.00058436, 0.0005846 ,\n", " 0.00058326, 0.00058377, 0.00058424, 0.00058334, 0.00058449,\n", " 0.00058263, 0.00058453, 0.00058237, 0.00058415, 0.00058463,\n", " 0.00058309, 0.00058493, 0.00058474, 0.00058511, 0.00058419,\n", " 0.00058459, 0.00058431, 0.00058398, 0.00058536, 0.00058361,\n", " 0.00058339, 0.00058352, 0.00058353, 0.00058478, 0.00058385,\n", " 0.00058499, 0.00058398]), 'inf_duration': 2.0633151531219482}\n" ] } ], "source": [ "# - Generate some Poisson input\n", "T = 1000\n", "f = 0.4\n", "input_spikes = np.random.rand(T, Nin) < f\n", "\n", "# - Evolve some input on the SNN core, and record power during inference\n", "out, _, record_dict = modSamna(input_spikes, record_power = True)\n", "\n", "print(record_dict)" ] }, { "cell_type": "raw", "id": "ef17e9bf", "metadata": { "raw_mimetype": "text/restructuredtext", "vscode": { "languageId": "raw" } }, "source": [ "For XyloAudio 2 power is sampled on four nets, and is returned in Watts.\n", "\n", "`'io_power'` is the total I/O power of the device.\n", "'`snn_core_power'` is the power consumption of the digital SNN core and control logic.\n", "'`afe_core_power'` is the power of the analog audio front-end core.\n", "'`afe_ldo_power'` is the power consumption of the internal low-drop-out voltage supply used by the AFE.\n", "\n", "For XyloAudio 3 power is sampled on three nets, and is returned in Watts.\n", "\n", "`'io_power'` is the total I/O power of the device.\n", "'`analog_power'` is the power of the analog audio front-end core.\n", "'`digital_power'` is the power consumption of the digital SNN core and control logic.\n" ] }, { "cell_type": "code", "execution_count": 5, "id": "8817900c-4286-4c55-bee5-cdd275ae5a1b", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "XyloAudio 3\n", "All IO:\t\t39.7 ”W\n", "AFE core:\t12.2 ”W\n", "SNN core logic:\t577.6 ”W\n" ] } ], "source": [ "# - Measure idle power (no evolution)\n", "from time import sleep\n", "import samna\n", "\n", "# For XyloA3 power measurement is activated at the beginning of the evolve function and deactivated at the end\n", "# to measure idle power, we need to activate power measurement explicitly\n", "if xylo_board_name == 'XyloAudio3':\n", " modSamna._power_monitor.start_auto_power_measurement(20)\n", "\n", "modSamna._power_buf.get_events()\n", "sleep(5.)\n", "power = modSamna._power_buf.get_events()\n", "\n", "# - For XyloAudio 2\n", "if xylo_board_name == 'XyloAudio2':\n", " power_idle = ([], [], [], [])\n", " for p in power:\n", " power_idle[p.channel].append(p.value)\n", "\n", " idle_power_per_channel = np.mean(np.stack(power_idle), axis = 1)\n", " \n", " channels = samna.xyloA2TestBoard.MeasurementChannels\n", " io_power = idle_power_per_channel[channels.Io]\n", " afe_core_power = idle_power_per_channel[channels.LogicAfe]\n", " afe_ldo_power = idle_power_per_channel[channels.IoAfe]\n", " snn_core_power = idle_power_per_channel[channels.Logic]\n", " \n", " print(f'XyloAudio 2\\nAll IO:\\t\\t{io_power * 1e6:.1f} ”W\\nAFE core:\\t{afe_core_power * 1e6:.1f} ”W\\nInternal LDO:\\t{afe_ldo_power * 1e6:.1f} ”W\\nSNN core logic:\\t{snn_core_power*1e6:.1f} ”W')\n", "\n", "# - For XyloAudio 3\n", "elif xylo_board_name == 'XyloAudio3':\n", " # deactivate power measurement\n", " modSamna._power_monitor.stop_auto_power_measurement()\n", " \n", " power_idle = ([], [], [])\n", " for p in power:\n", " power_idle[p.channel].append(p.value)\n", "\n", " channels = samna.xyloAudio3.MeasurementChannels\n", " idle_power_per_channel = np.mean(np.stack(power_idle), axis = 1)\n", " io_power = idle_power_per_channel[channels.Io] \n", " analog_power = idle_power_per_channel[channels.AnalogLogic] \n", " digital_power = idle_power_per_channel[channels.DigitalLogic]\n", " print(f'XyloAudio 3\\nAll IO:\\t\\t{io_power * 1e6:.1f} ”W\\nAFE core:\\t{analog_power * 1e6:.1f} ”W\\nSNN core logic:\\t{digital_power*1e6:.1f} ”W')\n" ] }, { "cell_type": "markdown", "id": "91e555e0", "metadata": {}, "source": [ "### About power measurement on XyloAudio 3" ] }, { "cell_type": "markdown", "id": "9559698f", "metadata": {}, "source": [ "XyloAudio 3 power is sampled in three nets:\n", "* IO power measures the power necessary to communicate with the outside world. \n", "* Analog power measures the power of the analog frontend and ADC.\n", "* Digital power measures the power consumption of the digital logic (XyloAudio 3 internal digital power, excluding IO power)\n", "\n", "The digital power consumption primarily depends on the clock speed, and the size of the network. The number of spikes generated per time step doesn't matter that much on power consumption.\n", "\n", "XyloAudio 3 digital microphone is fed by an external power source and is not included in the Xylo power measurements.\n", "\n", "The internal block on Xylo that receives the PDM signal and does the signal processing (Digital Front End) is powered by the same power rail as the SNN core. Those blocks are included in the power measurement.\n", "And while all digital logic in XyloAudio 3 is powered with the same power supply, and thus the static power (without any clock) of the DFE and SNN core can't be distinguished, it is possible to distinguish the dynamic powers between DFE and SNN Core, by:\n", "** feeding PDM_CLK externally, also feeding meaningful PDM data, disabling main clock -> we will get the DFE dynamic power\n", "** don't activate PDM_IF, feeding spikes via SAER_I to SNN Core -> we will get the SNN core dynamic power\n", "\n", "Also note that:\n", "\n", "When XyloAudio 3 is powered on and no clocks are provided (disabling main clock as well via FPGA settings), the digital measured power is the total DFE + SNN Core static power (P0).\n", "When deploying a network and feeding spikes via the SAER_I interface and running SNN core in parallel, the measured digital power (P1) is P0 + dynamic SNN core power -> P1 - P0 = dynamic SNN core power.\n", "Note: P1 contains minor power from SAER_I processing, it is considered to be very small and can be ignored, also P1 is measured with SNN core in Accelerated mode.\n", "When deploying a network and activating digital microphone path as normal, the measured power (P2) at VDDHD is P0 + dynamic SNN core power + dynamic DFE power -> P2 - P1 = dynamic DFE power.\n", "Note: P2 is measured with SNN core in real-time mode.\n", "\n", "\n", "Accelerated and real-time will differ in power consumption because accelerated mode will in most cases be in the active phase while real time mode will switch between active and idle." ] }, { "attachments": {}, "cell_type": "markdown", "id": "76b6c0d9", "metadata": {}, "source": [ "### Hints on reducing power consumption" ] }, { "cell_type": "raw", "id": "ca452a27", "metadata": { "raw_mimetype": "text/restructuredtext", "vscode": { "languageId": "raw" } }, "source": [ "Several points may be useful in reducing active power on XyloAudio.\n", "\n", "Note: ``Accelerated time`` is not the most power-saving mode of XyloAudio 3.\n", "For instance, the same network above would have consumed around 350 ”W of processing when ran in ``Real time`` mode.\n", "\n", "- Reducing the core clock frequency. The dev-kit utility function :func:`.devices.xylo.syns61201.xa2_devkit_utils.set_xylo_core_clock_freq` can be used to set a lower core clock frequency than the default of 50 Mhz, lowering power consumption proportionally. Depending on your network size and complexity you can most likely reduce the clock frequency and still guarantee real-time operation.\n", "\n", "- Reducing network activity and network size. The SNN core within Xylo only works as hard as it needs to. Once all neuron states have been updated, the core performs no operations until the next network time-step. This conserves energy.\n", "\n" ] }, { "cell_type": "code", "execution_count": 7, "id": "84ca7084", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Help on function set_xylo_core_clock_freq in module rockpool.devices.xylo.syns65302.xa3_devkit_utils:\n", "\n", "set_xylo_core_clock_freq(device: samna.xyloAudio3.XyloAudio3TestBoard, main_clock_frequency_MHz: float) -> None\n", " Set the internal core clock frequency used by Xylo\n", "\n", " Args:\n", " device (XyloAudio3HDK): A XyloAudio 3 device to configure\n", " main_clock_frequency_MHz (float): The main clock frequency of XyloAudio 3 in MHz\n", "\n" ] } ], "source": [ "if xylo_board_name == 'XyloAudio2':\n", " help(xa_utils.set_xylo_core_clock_freq)\n", "elif xylo_board_name == 'XyloAudio3':\n", " help(xa_utils.set_xylo_core_clock_freq) \n" ] }, { "cell_type": "markdown", "id": "3ee1b04f", "metadata": {}, "source": [ "### Estimate the required master clock frequency for real-time operation" ] }, { "cell_type": "raw", "id": "301e2b33", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ "Rockpool includes a model of the cycles required to operate the Xylo SNN inference core, in :py:func:`~.syns61201.cycles_model`. Xylo is an efficient device, which skips computation whenever possible. As a result, the computational requirements and power consumption vary dynamically with network activity, and with network configuration.\n", "\n", "By default :py:func:`~.syns61201.cycles_model` estimates a worst-case scenario, which can inform the master clock frequency that guarantees real-time operation for your network. It can also take into account real network activity to estimate a usual computational load.\n", "\n", "The function :py:func:`~.syns61201.est_clock_freq` will return the master clock frequency that guarantees real-time operation, for a given Xylo configuration." ] }, { "cell_type": "code", "execution_count": 8, "id": "2846eb5a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Help on function cycles_model in module rockpool.devices.xylo.syns63300.power_cycles_model:\n", "\n", "cycles_model(config: Union[samna.xyloImu.configuration.XyloConfiguration, samna.xyloAudio3.configuration.XyloConfiguration], input_sp: Union[float, numpy.ndarray, torch.Tensor, array] = 1.0, hidden_sp: Union[float, numpy.ndarray, torch.Tensor, array] = 1.0, output_sp: Union[float, numpy.ndarray, torch.Tensor, array] = 1.0) -> float\n", " Calculate the average number of cycles required for a given network architecture\n", "\n", " This function contains a model which estimates the number of master clock cycles required for the Xylo SNN SYNS61202 and SYNS65302 inference cores to compute one time-step for a given chip configuration in ``config``. Use :py:func:`~.devices.xylo.syns61201.config_from_specification` to obtain a chip configuration, along with :py:meth:`.Module.as_graph` and :py:func:`~.devices.xylo.syns61201.mapper`, as described in the deployment tutorials for Xylo.\n", "\n", " By default the model provides a \"worst-case\" estimation, assuming that every neuron and every input channel fire on each time-step. If desired, real input rasters and real hidden and output spike rasters can be provided for analysis. Alternative spiking probabilities can also be provided as floats ``0..1``.\n", "\n", " Note that when estimating spiking probablility, only boolean values are relevant --- either a spike or no spike per time step per channel. Multiple events per bin cost the same as a single event.\n", "\n", " Args:\n", " config (Union[XyloIMUConfig, XyloA3Config]): A Xylo configuration for which to calculate the cycle requirements\n", " input_sp (FloatVector): Either a floating-point number 0..1, specifying the average input firing rate, or an actual input spike raster to use in evaluation. Default: `1.0`; estimate a worst-case scenario\n", " hidden_sp (FloatVector): Either a floating-point number 0..1, specifying the average hidden neuron firing rate, or an actual hidden spike raster to use in evaluation. Default: `1.0`; estimate a worst-case scenario\n", " output_sp (FloatVector): Either a floating-point number 0..1, specifying the average output neuron firing rate, or an actual output spike raster to use in evaluation. Default: `1.0`; estimate a worst-case scenario\n", "\n", " Returns:\n", " float: The average number of master clock cycles required for this configuration, for the Xylo SNN core to compute one network `dt`\n", "\n" ] } ], "source": [ "if xylo_board_name == 'XyloAudio2':\n", " from rockpool.devices.xylo.syns61201 import cycles_model, est_clock_freq\n", "elif xylo_board_name == 'XyloAudio3':\n", " from rockpool.devices.xylo.syns65302 import cycles_model, est_clock_freq\n", "help(cycles_model)" ] }, { "cell_type": "code", "execution_count": 9, "id": "c8f6005a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Help on function est_clock_freq in module rockpool.devices.xylo.syns63300.power_cycles_model:\n", "\n", "est_clock_freq(config: Union[samna.xyloImu.configuration.XyloConfiguration, samna.xyloAudio3.configuration.XyloConfiguration], dt: float, margin: float = 0.2)\n", " Estimate the required master clock frequency, to run a network in real-time.\n", "\n", " This function will perform a worst-case analysis, assuming that every input channel, every hidden neuron and every output neuron fire an event on each `dt`.\n", " An additional margin is included (Default: 20%), to guarantee that the model will run in real time at the suggested master clock frequency.\n", " Note: The clock frequency is returned in Hz.\n", "\n", " Args:\n", " config (Union[XyloIMUConfig, XyloA3Config]): A Xylo configuration for which to estimate the required clock frequency\n", " dt (float): The required network `dt`, in seconds\n", " margin (float): The additional overhead safety margin to add to the estimation, as a fraction. Default: `0.2` (20%)\n", "\n" ] } ], "source": [ "help(est_clock_freq)" ] }, { "cell_type": "code", "execution_count": 10, "id": "202ebeae", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "This network requires 562.0 master clock cycles per network time-step.\n", "This network requires a master clock of 0.67 MHz for real-time operation.\n" ] } ], "source": [ "print(f\"This network requires {cycles_model(config)} master clock cycles per network time-step.\")\n", "print(f\"This network requires a master clock of {est_clock_freq(config, dt) / 1e6:.2f} MHz for real-time operation.\")" ] }, { "cell_type": "code", "execution_count": null, "id": "c0c220e9-3155-49ef-8fae-a48200a26ec2", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "rockpool", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.3" } }, "nbformat": 4, "nbformat_minor": 5 }