{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Writing Python for new Overlay\n",
    "\n",
    "This example will show how to interface to an overlay or hardware library from Python. \n",
    "\n",
    "In this example, we will assume a new overlay has been created with an accelerator that receives data from Python, processes it, and returns the results. \n",
    "\n",
    "A command and data will be sent to the accelerator from Python, the accelerator will process the data, return the results to memory, and acknowledge the transaction has completed.\n",
    "\n",
    "Rather than go through the process or creating a new overlay, for the purposes of this example, the Base overlay will be used to illustrate the process. The IOP1 memory will be used to act like the accelerator memory, although no processing will be carried out on the data.\n",
    "\n",
    "For this example, we will define the following addresses in the overlay, which are in the IOP1 memory space, and are accessible from Python:\n",
    "\n",
    "|Address                    | Name            | Memory Location |\n",
    "|---------------------------|-----------------|-----------------| \n",
    "|Accelerator address        | BASE_ADDRESS    | 0x40000000      |    \n",
    "|Command Address offset     | CMD_OFFSET      | 0x800           |\n",
    "|Acknowledge Address offset | ACK_OFFSET      | 0x804           |\n",
    "|Raw Data Address offset    | RAW_DATA_OFFSET | 0x0             |\n",
    "|Data Address offset        | DATA_OFFSET     | 0x400           |\n",
    "\n",
    "Assume we only have the following commands for this simple accelerator:\n",
    "\n",
    "|Command | Value| \n",
    "|--------|------| \n",
    "|Idle    | 0x0  |\n",
    "|Process | 0x1  |   "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Create a new Python module\n",
    "The pynq _MMIO_ module will be used to read and write to memory, or memory mapped peripherals in the Overlay. First _MMIO_ is imported, and then the new class for this module is defined. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```python\n",
    "from pynq import MMIO\n",
    "\n",
    "class my_new_accelerator:\n",
    "    \"\"\"Brief description of Module goes here\n",
    "    \n",
    "    Attributes\n",
    "    ----------\n",
    "    array_size : int\n",
    "        Describe  parameters used in this module's functions.\n",
    "    \"\"\"\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Instantiate the MMIO\n",
    "\n",
    "Next the ``MMIO`` will be instantiated inside the new module."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```python\n",
    "   mmio = MMIO(0x40000000,0x808)\n",
    "   array_length = 0\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that a variable, array_length, for this module will also be declared. You will see how this is used later."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Assume that the accelerator will check the command address when it starts.\n",
    "\n",
    "The Python module must first initialize the command location ``(BASE_ADDRESS + CMD_OFFSET)`` to 0x0 (\"idle\"). "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Declare an initialization function\n",
    "Declare the function and write zero to the command location:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```python\n",
    "   def __init__(self, array_size):\n",
    "      self.mmio.write(CMD_OFFSET, 0)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Define the API\n",
    "\n",
    "For this example, we will define two functions; ``load_data()`` and ``process()``. \n",
    "\n",
    "``load_data()`` will write data to the accelerator memory. \n",
    "\n",
    "``process_data()`` will send the start command to the accelerator, wait for an acknowledge, and read back the processed data.\n",
    "\n",
    "* 0x1 will be written to the _command_ location from Python\n",
    "* The accelerator will write 0x1 to the _acknowledge_ location when processing is complete.\n",
    "\n",
    "Note how the ``array_length`` variable is used."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```python\n",
    "def load_data(self, raw_data):\n",
    "    self.array_length = len(raw_data)\n",
    "    for i in range(0 , self.array_length):\n",
    "        self.mmio.write(RAW_DATA_OFFSET, raw_data[i])\n",
    "    \n",
    "def process(self):     \n",
    "    # Send start command to accelerator\n",
    "    self.mmio.write(CMD_OFFSET, 0x1)\n",
    "    processed_data = [0] *self.array_length\n",
    "    #ACK is set to check for 0x0 in the ACK offset\n",
    "    while (self.mmio.read(ACK_OFFSET)) != 0x1:\n",
    "        pass\n",
    "    # Ack has been received\n",
    "    \n",
    "    for i in range(0 , self.array_length):\n",
    "        processed_data[i] = self.mmio.read(PROCESSED_DATA_OFFSET)\n",
    "        \n",
    "    # Reset Ack\n",
    "    self.mmio.write(ACK_OFFSET, 0)      \n",
    "    return processed_data\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Final code\n",
    "The complete code can be found below, and can be executed and tested in this notebook by running the cells below. The code could be copied to a python file, and run directly on the board. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "BASE_ADDRESS = 0x40000000\n",
    "CMD_OFFSET = 0x800\n",
    "ACK_OFFSET = 0x804\n",
    "RAW_DATA_OFFSET = 0\n",
    "PROCESSED_DATA_OFFSET = 0x400\n",
    "        \n",
    "from pynq import MMIO\n",
    "  \n",
    "class my_new_accelerator:\n",
    "    \"\"\"Brief description of Module goes here.\n",
    "    \n",
    "    Attributes\n",
    "    ----------\n",
    "    array_size : int\n",
    "       Describe  parameters used in this module's functions.\n",
    "    raw_data : int\n",
    "       Input Data\n",
    "    processed_data : int\n",
    "       Return data\n",
    "       \n",
    "   \"\"\"\n",
    "    mmio = MMIO(0x40000000,0x808)\n",
    "    array_length = 0\n",
    " \n",
    "    def __init__(self):\n",
    "        self.mmio.write(CMD_OFFSET, 0)\n",
    "     \n",
    "    def load_data(self, raw_data):\n",
    "        self.array_length = len(raw_data)\n",
    "        for i in range(0 , self.array_length):\n",
    "            self.mmio.write(RAW_DATA_OFFSET, raw_data[i])\n",
    "            \n",
    "    def process(self):     \n",
    "        # Send start command to accelerator\n",
    "        self.mmio.write(CMD_OFFSET, 0x1)\n",
    "        processed_data = [0] *self.array_length\n",
    "        \n",
    "        # ACK is set to check for 0x0 in the ACK offset\n",
    "        while (self.mmio.read(ACK_OFFSET)) != 0x1:\n",
    "            pass\n",
    "        # Ack has been received\n",
    "\n",
    "        for i in range(0 , self.array_length):\n",
    "            processed_data[i] = self.mmio.read(PROCESSED_DATA_OFFSET)\n",
    "            \n",
    "        # Reset Ack\n",
    "        self.mmio.write(ACK_OFFSET, 0)      \n",
    "        return processed_data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Executing the cell above loads the module into this notebook. This is the equivalent of importing the module (``import my_new_accelerator``) if it was included as part of the pynq package.\n",
    "\n",
    "As explained previously, this notebook does not show you how to create a custom accelerator, however, the python code can be tested with the Base overlay. In the Base overlay, the IOP memory (starting at 0x40000000) will be used to simulate writing to an accelerator, and reading back from the accelerator. Notice how the code writes to one area of memory (BASE_ADDRESS + RAW_DATA_OFFSET), and expects to read back results from another area in memory (BASE_ADDRESS + PROCESSED_DATA_OFFSET). \n",
    "\n",
    "Execute the cell below to load the Pmod overlay, instantiate the accelerator, and send some data to the accelerator."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Some data to be sent to the accelerator: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]\n"
     ]
    }
   ],
   "source": [
    "from pynq import Overlay\n",
    "Overlay(\"base.bit\").download()\n",
    "\n",
    "# declare acc with a Maximum allowable array size\n",
    "acc = my_new_accelerator()\n",
    "raw_data = [1]*20\n",
    "print(\"Some data to be sent to the accelerator:\", raw_data)\n",
    "acc.load_data(raw_data)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As the accelerator doesn't exist, any data loaded to memory won't be processed, and the acknowledge will not be written. \n",
    "\n",
    "Execute the cell below to use the ``MMIO`` to manually write some data to the results area of the memory to simulate data being processed, and to write 0x1 to the acknowledge address. \n",
    "\n",
    "The ``MMIO`` can be very useful to peak and poke memory and memory mapped peripherals in the overlay to debug Python code."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "from pynq import MMIO\n",
    "       \n",
    "mmio = MMIO(0x40000000,2056)\n",
    "\n",
    "for i in range (0,len(raw_data)):\n",
    "    mmio.write(PROCESSED_DATA_OFFSET, raw_data[i]+1)\n",
    "\n",
    "for i in range (0,len(raw_data)):\n",
    "    mmio.write(ACK_OFFSET, 1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The ``process()`` function can now send a start command, read the acknowledge (which has already been set manually in the cell above), and read back from data from the processed data area. You can change the code above to write different data to the processed data area, or to set the acknowlege to 0 (which will cause the code below to hang)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Input Data     :  [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]\n",
      "Processed Data :  [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]\n"
     ]
    }
   ],
   "source": [
    "processed_data = acc.process()\n",
    "print(\"Input Data     : \", raw_data)\n",
    "print(\"Processed Data : \", processed_data)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "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.4.3+"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
