{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "source": [
    "<link rel=\"preconnect\" href=\"https://fonts.gstatic.com\">\n",
    "<link href=\"https://fonts.googleapis.com/css2?family=Fira+Code&display=swap\" rel=\"stylesheet\">\n",
    "\n",
    "### License\n",
    "\n",
    "<p style=\"font-family: 'Fira Code', monospace; font-size: 1.2rem\">\n",
    "Copyright 2021 Xilinx, Inc.<br><br>\n",
    "Licensed under the Apache License, Version 2.0 (the \"License\");<br>\n",
    "you may not use this file except in compliance with the License.<br><br>\n",
    "You may obtain a copy of the License at <a href=\"http://www.apache.org/licenses/LICENSE-2.0\"?>http://www.apache.org/licenses/LICENSE-2.0</a><br><br>\n",
    "Unless required by applicable law or agreed to in writing, software<br>\n",
    "distributed under the License is distributed on an \"AS IS\" BASIS,<br>\n",
    "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.<br>\n",
    "See the License for the specific language governing permissions and<br>\n",
    "limitations under the License.<br>\n",
    "</p>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# ChipScoPy Memory Read and Write Example\n",
    "\n",
    "\n",
    "<img src=\"../img/api_overview.png\" width=\"500\" align=\"left\">"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Description\n",
    "This demo shows how to read and write memory in the device using the ChipScoPy Python API.\n",
    "\n",
    "\n",
    "## Requirements\n",
    "- Local or remote Xilinx Versal board, such as a VCK190\n",
    "- Xilinx hw_server 2022.1 installed and running\n",
    "- Python 3.8 or greater installed\n",
    "- ChipScoPy 2022.1 installed\n",
    "- Jupyter notebook support installed - Please do so, using the command `pip install chipscopy[jupyter]`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1 - Initialization: Imports and File Paths\n",
    "\n",
    "After this step,\n",
    "- Required functions and classes are imported\n",
    "- Paths to server(s) and files are set correctly"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "import os\n",
    "from chipscopy import get_design_files\n",
    "from chipscopy import create_session, report_versions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "HW_URL=TCP:localhost:3121\n"
     ]
    }
   ],
   "source": [
    "# Make sure to start the hw_server prior to running.\n",
    "# Specify location of the running hw_server below.\n",
    "# The default is localhost - but can be other locations on the network.\n",
    "HW_URL = os.getenv(\"HW_SERVER_URL\", \"TCP:localhost:3121\")\n",
    "print(f\"HW_URL={HW_URL}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2 - Create a session and connect to the hw_server\n",
    "\n",
    "The session is a container that keeps track of devices and debug cores.\n",
    "After this step,\n",
    "- Session is initialized and connected to server(s)\n",
    "- Versions are detected and reported to stdout\n",
    "\n",
    "*NOTE*: No cs_server is required for this example."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\">\n",
       "\n",
       "</pre>\n"
      ],
      "text/plain": [
       "<rich.jupyter.JupyterRenderable at 0x1a32d69d970>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-style: italic\">                         ChipScoPy Version Information                          </span>\n",
       "┌────────────────────────────┬─────────────────────────────────────────────────┐\n",
       "│<span style=\"font-weight: bold\">                     Entity </span>│<span style=\"font-weight: bold\"> Version                                         </span>│\n",
       "├────────────────────────────┼─────────────────────────────────────────────────┤\n",
       "│                  ChipScoPy │ ┌───────────┬──────────────────────┐            │\n",
       "│                            │ │<span style=\"font-weight: bold\"> Attribute </span>│<span style=\"font-weight: bold\"> Value                </span>│            │\n",
       "│                            │ ├───────────┼──────────────────────┤            │\n",
       "│                            │ │     Build │ 2022.1.1654231490    │            │\n",
       "│                            │ │ Timestamp │ Jun 02 2022-22:44:50 │            │\n",
       "│                            │ └───────────┴──────────────────────┘            │\n",
       "│                            │                                                 │\n",
       "│ hw_server @ localhost:3121 │ ┌───────────┬─────────────────────────────────┐ │\n",
       "│                            │ │<span style=\"font-weight: bold\"> Attribute </span>│<span style=\"font-weight: bold\"> Value                           </span>│ │\n",
       "│                            │ ├───────────┼─────────────────────────────────┤ │\n",
       "│                            │ │    Status │ Connected                       │ │\n",
       "│                            │ │     Build │ 1641456835                      │ │\n",
       "│                            │ │   Version │ 2022.1                          │ │\n",
       "│                            │ │ Timestamp │ Thu 06 Jan 2022 08:13:55 AM UTC │ │\n",
       "│                            │ └───────────┴─────────────────────────────────┘ │\n",
       "└────────────────────────────┴─────────────────────────────────────────────────┘\n",
       "</pre>\n"
      ],
      "text/plain": [
       "<rich.jupyter.JupyterRenderable at 0x1a32d69d970>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\">\n",
       "\n",
       "</pre>\n"
      ],
      "text/plain": [
       "<rich.jupyter.JupyterRenderable at 0x1a32d69d970>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "session = create_session(hw_server_url=HW_URL)\n",
    "report_versions(session)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 3 - Get the device from the session"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "xcvc1902:0xc00000000000000001566fc10ae04282:jsn-VCK190 FT4232H-282203141805A-14ca8093-0\n"
     ]
    }
   ],
   "source": [
    "# Typical case - one device on the board - get it.\n",
    "versal_device = session.devices.filter_by(family=\"versal\").get()\n",
    "print(versal_device)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 4 - Reset the device"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Reset complete.\n"
     ]
    }
   ],
   "source": [
    "versal_device.reset()\n",
    "print(\"Reset complete.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 5 - Write and Read memory\n",
    "\n",
    "\n",
    "ChipScoPy can be used to read and write memory using the hardware server.\n",
    "Memory reads and writes work similar to xsdb mrd and mwr commands.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Show the list of all memory targets\n",
    "\n",
    "Memory targets in this list can be used for memory_read and memory_write\n",
    "operations."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Memory Targets:  ['DPC', 'Versal xcvc1902', 'PSM', 'PPU', 'MicroBlaze PPU', 'APU', 'Cortex-A72 #1', 'Cortex-A72 #0', 'RPU', 'Cortex-R5 #1', 'Cortex-R5 #0']\n"
     ]
    }
   ],
   "source": [
    "print(\"\\nMemory Targets: \", versal_device.memory_target_names)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Simple Write and read memory example\n",
    "\n",
    "This is the most basic memory_read and memory_write example using the default\n",
    "DPC memory target.\n",
    "\n",
    "Below we write 32-bit values to the specified address and read them back."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Writing [0x10111213, 0x14151617] to address: 0xf2010000\n",
      "Reading 2 values from address: hex(addr)\n",
      "Readback result: [0x10111213, 0x14151617]\n"
     ]
    }
   ],
   "source": [
    "addr = 0xF2010000\n",
    "values_to_write = [0x10111213, 0x14151617]\n",
    "\n",
    "print(\"\\nWriting [{}]\".format(\", \".join(hex(x) for x in values_to_write)), \"to address:\", hex(addr))\n",
    "# Write to the DPC default target\n",
    "versal_device.memory_write(addr, values_to_write)\n",
    "\n",
    "print(f\"Reading {len(values_to_write)} values from address: hex(addr)\")\n",
    "read_values = versal_device.memory_read(address=addr, num=len(values_to_write))\n",
    "\n",
    "print(\"Readback result: [{}]\".format(\", \".join(hex(x) for x in read_values)))\n",
    "\n",
    "assert read_values == values_to_write"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Changing Memory Read/Write Word Sizes\n",
    "\n",
    "It is possible to specify the word size when reading and writing.\n",
    "Default is 'w'. Other sizes shown below.\n",
    "```\n",
    "'b'=byte\n",
    "'h'=half\n",
    "'w'=word\n",
    "'d'=double word\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Writing [0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17] to address: 0xf2010000\n",
      "Reading from address:  0xf2010000\n",
      "Readback result: [0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17]\n",
      "\n",
      "Writing [0x1000, 0x1234, 0x4321, 0x1313, 0x1414, 0x1515, 0x1616, 0x1717] to address: 0xf2010000\n",
      "Reading from address:  0xf2010000\n",
      "Readback result: [0x1000, 0x1234, 0x4321, 0x1313, 0x1414, 0x1515, 0x1616, 0x1717]\n"
     ]
    }
   ],
   "source": [
    "addr = 0xF2010000\n",
    "values_to_write = [0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17]\n",
    "\n",
    "print(\"\\nWriting [{}]\".format(\", \".join(hex(x) for x in values_to_write)), \"to address:\", hex(addr))\n",
    "versal_device.memory_write(addr, values_to_write, size=\"b\")\n",
    "\n",
    "print(\"Reading from address: \", hex(addr))\n",
    "read_values = versal_device.memory_read(address=addr, size=\"b\", num=len(values_to_write))\n",
    "print(\"Readback result: [{}]\".format(\", \".join(hex(x) for x in read_values)))\n",
    "assert read_values == values_to_write\n",
    "\n",
    "values_to_write = [0x1000, 0x1234, 0x4321, 0x1313, 0x1414, 0x1515, 0x1616, 0x1717]\n",
    "print(\"\\nWriting [{}]\".format(\", \".join(hex(x) for x in values_to_write)), \"to address:\", hex(addr))\n",
    "versal_device.memory_write(addr, values_to_write, size=\"h\")\n",
    "\n",
    "print(\"Reading from address: \", hex(addr))\n",
    "read_values = versal_device.memory_read(address=addr, size=\"h\", num=len(values_to_write))\n",
    "print(\"Readback result: [{}]\".format(\", \".join(hex(x) for x in read_values)))\n",
    "assert read_values == values_to_write"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Selecting different memory targets and improving performance\n",
    "\n",
    "The examples above use the device class for memory_read() and memory_write()\n",
    "operations. Using the device read and write is simple, but has additional\n",
    "overhead with each call to find the associated memory context.\n",
    "\n",
    "It is possible to explicitly request the memory context for a desired target.\n",
    "Once a memory context is obtained, memory_read and memory_write operations\n",
    "can be executed on that target repeatedly.\n",
    "\n",
    "This eliminate some of the additional overhead.\n",
    "\n",
    "The example below shows how to get a context to repeatedly read and write from\n",
    "different memory targets."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "DPC: Writing [0x12345678, 0xfedcba98] to address: 0xf2010000\n",
      "APU: Reading from address:  0xf2010000\n",
      "Readback result: [0x12345678, 0xfedcba98]\n",
      "\n",
      "DPC: Writing [0x12345679, 0xfedcba97] to address: 0xf2010000\n",
      "APU: Reading from address:  0xf2010000\n",
      "Readback result: [0x12345679, 0xfedcba97]\n",
      "\n",
      "DPC: Writing [0x1234567a, 0xfedcba96] to address: 0xf2010000\n",
      "APU: Reading from address:  0xf2010000\n",
      "Readback result: [0x1234567a, 0xfedcba96]\n",
      "\n",
      "DPC: Writing [0x1234567b, 0xfedcba95] to address: 0xf2010000\n",
      "APU: Reading from address:  0xf2010000\n",
      "Readback result: [0x1234567b, 0xfedcba95]\n",
      "\n",
      "DPC: Writing [0x1234567c, 0xfedcba94] to address: 0xf2010000\n",
      "APU: Reading from address:  0xf2010000\n",
      "Readback result: [0x1234567c, 0xfedcba94]\n",
      "\n",
      "DPC: Writing [0x1234567d, 0xfedcba93] to address: 0xf2010000\n",
      "APU: Reading from address:  0xf2010000\n",
      "Readback result: [0x1234567d, 0xfedcba93]\n",
      "\n",
      "DPC: Writing [0x1234567e, 0xfedcba92] to address: 0xf2010000\n",
      "APU: Reading from address:  0xf2010000\n",
      "Readback result: [0x1234567e, 0xfedcba92]\n",
      "\n",
      "DPC: Writing [0x1234567f, 0xfedcba91] to address: 0xf2010000\n",
      "APU: Reading from address:  0xf2010000\n",
      "Readback result: [0x1234567f, 0xfedcba91]\n",
      "\n",
      "DPC: Writing [0x12345680, 0xfedcba90] to address: 0xf2010000\n",
      "APU: Reading from address:  0xf2010000\n",
      "Readback result: [0x12345680, 0xfedcba90]\n",
      "\n",
      "DPC: Writing [0x12345681, 0xfedcba8f] to address: 0xf2010000\n",
      "APU: Reading from address:  0xf2010000\n",
      "Readback result: [0x12345681, 0xfedcba8f]\n"
     ]
    }
   ],
   "source": [
    "addr = 0xF2010000\n",
    "dpc = versal_device.memory.get(name=\"DPC\")\n",
    "apu = versal_device.memory.get(name=\"APU\")\n",
    "for i in range(10):\n",
    "    values_to_write = [0x12345678 + i, 0xFEDCBA98 - i]\n",
    "    # Write to the DPC without context lookup overhead\n",
    "    print(\n",
    "        \"\\nDPC: Writing [{}]\".format(\", \".join(hex(x) for x in values_to_write)),\n",
    "        \"to address:\",\n",
    "        hex(addr),\n",
    "    )\n",
    "    dpc.memory_write(addr, values_to_write)\n",
    "    # Read from the APU without context lookup overhead\n",
    "    print(\"APU: Reading from address: \", hex(addr))\n",
    "    read_values = apu.memory_read(address=addr, num=len(values_to_write))\n",
    "    print(\"Readback result: [{}]\".format(\", \".join(hex(x) for x in read_values)))\n",
    "    assert read_values == values_to_write"
   ]
  }
 ],
 "metadata": {
  "jupytext": {
   "cell_metadata_filter": "pycharm,-all",
   "main_language": "python",
   "notebook_metadata_filter": "-all"
  },
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "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.8.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
