{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "c773ac0c",
      "metadata": {},
      "source": [
        "### License\n",
        "Copyright (C) 2021-2022, Xilinx, Inc.\n",
        "<br>\n",
        "Copyright (C) 2022-2024, Advanced Micro Devices, Inc.\n",
        "<p>\n",
        "Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "you may not use this file except in compliance with the License.\n",
        "<p>\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\n",
        "distributed under the License is distributed on an \"AS IS\" BASIS, \n",
        "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "See the License for the specific language governing permissions and\n",
        "limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1fbadbb5",
      "metadata": {},
      "source": [
        "# IBERT yk scan example"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2751e658",
      "metadata": {},
      "source": [
        "## Description\n",
        "This example shows how to interact with the IBERT (Integrated Bit Error Ratio Tester) debug core service via ChipScoPy APIs.\n",
        "- Program the ChipScoPy CED design onto the XCVP1202 device on a VPK120 board\n",
        "- Verify that the expected IBERT quads are instantiated by the design\n",
        "- Run and plot YK scans\n",
        "\n",
        "## Requirements\n",
        "- Local or remote Xilinx Versal board, VPK120 or VHK158 (only)\n",
        "- Xilinx hw_server 2024.1 installed and running\n",
        "- Xilinx cs_server 2024.1 installed and running\n",
        "- Python 3.8 or greater installed\n",
        "- ChipScoPy 2024.1 installed\n",
        "- Jupyter notebook support installed - Please do so, using the command `pip install chipscopy[jupyter]`\n",
        "- Plotting support installed - Please do so, using the command `pip install chipscopy[core-addons]`\n",
        "- [External loopback](https://www.samtec.com/kits/optics-fpga/hspce-fmcp/)\n",
        "- This example assumes that the device has already been programmed with the example design (ie the debug cores have already been initialized)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4f62a13a",
      "metadata": {},
      "source": [
        "## 1 - Initialization: Imports\n",
        "\n",
        "After this step,\n",
        "\n",
        "* Required functions and classes are imported\n",
        "* Paths to server(s) and files are set correctly"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "89c65a06",
      "metadata": {},
      "outputs": [],
      "source": [
        "import os\n",
        "from more_itertools import one\n",
        "import matplotlib.pyplot as plt\n",
        "\n",
        "from chipscopy import (\n",
        "    create_session,\n",
        "    report_versions,\n",
        "    report_hierarchy,\n",
        "    get_design_files,\n",
        "    delete_session,\n",
        ")\n",
        "from chipscopy.api.ibert import create_yk_scans"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "990a87bc",
      "metadata": {},
      "source": [
        "## 2 - Create a session and connect to the hw_server and cs_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"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "a5562a9f",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Specify locations of the running hw_server and cs_server below.\n",
        "CS_URL = os.getenv(\"CS_SERVER_URL\", \"TCP:localhost:3042\")\n",
        "HW_URL = os.getenv(\"HW_SERVER_URL\", \"TCP:localhost:3121\")\n",
        "\n",
        "# specify hw and if programming is desired\n",
        "HW_PLATFORM = os.getenv(\"HW_PLATFORM\", \"vpk120\")\n",
        "PROG_DEVICE = os.getenv(\"PROG_DEVICE\", 'True').lower() in ('true', '1', 't')\n",
        "\n",
        "# The get_design_files() function tries to find the PDI and LTX files. In non-standard\n",
        "# configurations, you can put the path for PROGRAMMING_FILE and PROBES_FILE below.\n",
        "design_files = get_design_files(f\"{HW_PLATFORM}/production/chipscopy_ced\")\n",
        "PDI_FILE = design_files.programming_file\n",
        "\n",
        "print(f\"PROGRAMMING_FILE: {PDI_FILE}\")\n",
        "\n",
        "session = create_session(cs_server_url=CS_URL, hw_server_url=HW_URL)\n",
        "report_versions(session)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "fae15151",
      "metadata": {},
      "source": [
        "## 3 - Program the device with the example design\n",
        "After this step,\n",
        "* Device is programmed with the example programming file"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "8a597d97",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Typical case - one device on the board - get it.\n",
        "device = session.devices.filter_by(family=\"versal\").get()\n",
        "if PROG_DEVICE:\n",
        "    device.program(PDI_FILE)\n",
        "else:\n",
        "    print(\"skipping programming\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7c38f075",
      "metadata": {},
      "source": [
        "## 4 - Discover and setup the IBERT core\n",
        "\n",
        "Debug core discovery initializes the chipscope server debug cores.\n",
        "\n",
        "After this step,\n",
        "\n",
        "- The cs_server is initialized and ready for use\n",
        "- The first ibert found is used"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "0bf7bf19",
      "metadata": {},
      "outputs": [],
      "source": [
        "# # Set any params as needed\n",
        "# params_to_set = {\"IBERT.internal_mode\": True}\n",
        "# session.set_param(params_to_set)\n",
        "device = session.devices.filter_by(family=\"versal\").get()\n",
        "\n",
        "# Use the first available device and set up its debug cores\n",
        "\n",
        "print(f\"Discovering debug cores...\")\n",
        "device.discover_and_setup_cores(ibert_scan=True)\n",
        "\n",
        "if len(device.ibert_cores) == 0:\n",
        "    print(\"No IBERT core found! Exiting...\")\n",
        "    exit()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6086df97",
      "metadata": {},
      "source": [
        "## 5 - Print the hierarchy of the IBERT core\n",
        "We also ensure that all the quads instantiated by the ChipScoPy CED design are found by the APIs"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "67031982",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Use the first available IBERT core from the device\n",
        "print(f\"--> Found {[f'{ibert.name} ({ibert.handle})' for ibert in device.ibert_cores]}\\n\")\n",
        "\n",
        "ibert_gtm = one(device.ibert_cores.filter_by(name=\"IBERT Versal GTM\"))\n",
        "\n",
        "if len(ibert_gtm.gt_groups) == 0:\n",
        "    print(\"No GT Groups available for use! Exiting...\")\n",
        "    exit()\n",
        "\n",
        "print(f\"--> Enabled GT Groups - {ibert_gtm.gt_groups}\")\n",
        "\n",
        "gt_group = ibert_gtm.gt_groups.filter_by(name=\"Quad_204\")[0]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "10433957",
      "metadata": {},
      "source": [
        "## 6 - Define YK Scan Update Method\n",
        "\n",
        "This method will be called each time the yk scan updates, allowing it to update its graphs in real time. "
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "878154ff",
      "metadata": {},
      "outputs": [],
      "source": [
        "%matplotlib widget\n",
        "\n",
        "def yk_scan_updates(obj):\n",
        "    global count, figure, ax, ax2, ax3\n",
        "    if ax.lines:\n",
        "        for line in ax.lines:\n",
        "            line.set_xdata(range(len(obj.scan_data[-1].slicer)))\n",
        "            line.set_ydata(list(obj.scan_data[-1].slicer))\n",
        "    else:\n",
        "        ax.scatter(range(len(obj.scan_data[-1].slicer)), list(obj.scan_data[-1].slicer), color='blue')\n",
        "    \n",
        "    if ax2.lines:\n",
        "        for line2 in ax2.lines:\n",
        "            ax2.set_xlim(0, ax2.get_xlim()[1] + len(obj.scan_data[-1].slicer))\n",
        "            line2.set_xdata(list(line2.get_xdata()) + list(range(len(line2.get_xdata()), len(line2.get_xdata()) + len(obj.scan_data[-1].slicer))))\n",
        "            line2.set_ydata(list(line2.get_ydata()) + list(obj.scan_data[-1].slicer))\n",
        "    else:\n",
        "        ax2.scatter(range(len(obj.scan_data[-1].slicer)), list(obj.scan_data[-1].slicer), color='blue')\n",
        "        \n",
        "    if ax3.lines:\n",
        "        for line3 in ax3.lines:\n",
        "            if len(obj.scan_data) - 1 > ax3.get_xlim()[1]:\n",
        "                ax3.set_xlim(0, ax3.get_xlim()[1]+10)\n",
        "            line3.set_xdata(list(line3.get_xdata()) + [len(obj.scan_data) - 1])\n",
        "            line3.set_ydata(list(line3.get_ydata()) + [obj.scan_data[-1].snr])\n",
        "    else:\n",
        "        ax3.plot(len(obj.scan_data) - 1, obj.scan_data[-1].snr)\n",
        "\n",
        "\n",
        "    figure.canvas.draw_idle()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "20dd65e7",
      "metadata": {},
      "source": [
        "## 7 - Create YK Scan\n",
        "\n",
        "This step initializes the YK scan, setting its update method to the method we defined in the last step. "
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "9aec742d",
      "metadata": {},
      "outputs": [],
      "source": [
        "yk = create_yk_scans(target_objs=gt_group.gts[0].rx)[0]\n",
        "\n",
        "yk.updates_callback = yk_scan_updates"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "836b0ef2",
      "metadata": {},
      "source": [
        "## 8 - Run YK Scan\n",
        "\n",
        "Initialize the plots and start the YK Scan to begin updating the plots. \n",
        "YK Scan plot should contain three subplots, these plots should look something like:\n",
        "![yk_scan_example.png](./yk_scan_example.png)\n",
        "Note: Depending on the hardware setup and external loopback connection, the plot might look different."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "cdb98b19",
      "metadata": {},
      "outputs": [],
      "source": [
        "%matplotlib widget\n",
        "\n",
        "#This sets up the subplots necessary for the \n",
        "figure, (ax, ax2, ax3) = plt.subplots(3, constrained_layout = True, num=\"YK Scan\")\n",
        "\n",
        "ax.set_xlabel(\"ES Sample\")\n",
        "ax.set_ylabel(\"Amplitude (%)\")\n",
        "ax.set_xlim(0,2000)\n",
        "ax.set_ylim(0,100)\n",
        "ax.set_yticks(range(0, 100, 20))\n",
        "ax.set_title(\"Slicer eye\")\n",
        "\n",
        "ax2.set_xlabel(\"Count\")\n",
        "ax2.set_ylabel(\"Amplitude (%)\")\n",
        "ax2.set_xlim(0,2000)\n",
        "ax2.set_ylim(0,100)\n",
        "ax2.set_yticks(range(0, 100, 20))\n",
        "ax2.set_title(\"Histogram\")\n",
        "\n",
        "ax3.set_xlabel(\"SNR Sample\")\n",
        "ax3.set_ylabel(\"SNR (dB)\")\n",
        "ax3.set_xlim(0,10)\n",
        "ax3.set_ylim(-10,100)\n",
        "ax3.set_title(\"Signal-to-Noise Ratio\")\n",
        "\n",
        "yk.start()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "fa382983",
      "metadata": {},
      "source": [
        "## 9 - Stop YK Scan\n",
        "Stops the YK scan from running."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "2f070a7a",
      "metadata": {},
      "outputs": [],
      "source": [
        "yk.stop()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "ff6ae323",
      "metadata": {},
      "outputs": [],
      "source": [
        "## When done with testing, close the connection\n",
        "delete_session(session)"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "venv",
      "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.11.2"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}
