diff --git a/.gitignore b/.gitignore
index ac2138791..03157b502 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,7 +24,9 @@ waves.shm
 *.order
 *.symvers
 .vscode/
-
+venv
+*_wrapper.c
+fpga_utils.c
 *~
 .\#*
 \#*
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 7869c5030..98925aed0 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -1,5 +1,14 @@
 # F2 Developer Kit Release Notes
 
+## v2.1.2
+
+* Introduced Python Bindings to the SDK
+* Added [documentation](./sdk/userspace/cython_bindings/README.md) for Python binding usage and setup
+* [Examples](./sdk/userspace/cython_bindings/) demonstrating Python-based FPGA control
+* Added link to instructions for DCV licensing setup. Credit to @morgnza for this update!
+* Added verbiage to DCV setup guide to show where to set virtual display resolution
+* Fix to Bandwidth Calculation
+
 ## v2.1.1
 
 * Added global register offset for the SDE IP. See [CL_SDE software examples](./hdk/cl/examples/cl_sde/software/src/README.md).
diff --git a/developer_resources/Amazon_DCV_Setup_Guide.md b/developer_resources/Amazon_DCV_Setup_Guide.md
index 9a22bc155..cf1921849 100644
--- a/developer_resources/Amazon_DCV_Setup_Guide.md
+++ b/developer_resources/Amazon_DCV_Setup_Guide.md
@@ -31,10 +31,11 @@ graphical user interface (GUI) to visualize FPGA development in the cloud.
 
 ### Prerequisites
 
-1. [Depenency Installation](https://docs.aws.amazon.com/dcv/latest/adminguide/setting-up-installing-linux-prereq.html#linux-prereq-gui)
-    * :warning: DO NOT PERFORM STEP 3! Upgrading may impact the stability of development kit software!
-2. [Protocol Setup](https://docs.aws.amazon.com/dcv/latest/adminguide/setting-up-installing-linux-prereq.html#linux-prereq-wayland)
-3. [Driver Installation](https://docs.aws.amazon.com/dcv/latest/adminguide/setting-up-installing-linux-prereq.html#linux-prereq-nongpu)
+1. [Instance and IAM Configuration for DCV Licensing](https://docs.aws.amazon.com/dcv/latest/adminguide/setting-up-license.html#dcv-lic-req)
+2. [Depenency Installation](https://docs.aws.amazon.com/dcv/latest/adminguide/setting-up-installing-linux-prereq.html#linux-prereq-gui)
+    - :warning: DO NOT PERFORM STEP 3! Upgrading may impact the stability of development kit software!
+3. [Protocol Setup](https://docs.aws.amazon.com/dcv/latest/adminguide/setting-up-installing-linux-prereq.html#linux-prereq-wayland)
+4. [Driver Installation and Setting Virtual Display Resolution](https://docs.aws.amazon.com/dcv/latest/adminguide/setting-up-installing-linux-prereq.html#linux-prereq-nongpu)
 
 ### Amazon DCV Server Installation
 
diff --git a/docs-rtd/source/RELEASE-NOTES.rst b/docs-rtd/source/RELEASE-NOTES.rst
index 8932132ff..4417a62eb 100644
--- a/docs-rtd/source/RELEASE-NOTES.rst
+++ b/docs-rtd/source/RELEASE-NOTES.rst
@@ -1,6 +1,18 @@
 F2 Developer Kit Release Notes
 ==============================
 
+.. _v212:
+
+v2.1.2
+------
+
+- Introduced Python Bindings to the SDK
+- Added `documentation <./sdk/userspace/cython-bindings/README.html>` for Python binding usage and setup
+- `Examples <https://github.com/aws/aws-fpga/blob/f2/sdk/userspace/cython_bindings>` demonstrating Python-based FPGA control
+- Added link to instructions for DCV licensing setup. Credit to @morgnza for this update!
+- Added verbiage to DCV setup guide to show where to set virtual display resolution
+- Fix to Bandwidth Calculation
+
 .. _v211:
 
 v2.1.1
diff --git a/docs-rtd/source/developer-resources/Amazon-DCV-Setup-Guide.rst b/docs-rtd/source/developer-resources/Amazon-DCV-Setup-Guide.rst
index f17c310ca..2c899501e 100644
--- a/docs-rtd/source/developer-resources/Amazon-DCV-Setup-Guide.rst
+++ b/docs-rtd/source/developer-resources/Amazon-DCV-Setup-Guide.rst
@@ -49,16 +49,17 @@ Installing the Amazon DCV Server on an Amazon EC2 Instance
 Prerequisites
 ~~~~~~~~~~~~~
 
-1. `Depenency
+1. `Instance and IAM Configuration for DCV Licensing <https://docs.aws.amazon.com/dcv/latest/adminguide/setting-up-license.html#dcv-lic-req>`__
+2. `Depenency
    Installation <https://docs.aws.amazon.com/dcv/latest/adminguide/setting-up-installing-linux-prereq.html#linux-prereq-gui>`__
 
    - ⚠️ DO NOT PERFORM STEP 3! Upgrading may impact the stability of
      development kit software!
 
-2. `Protocol
+3. `Protocol
    Setup <https://docs.aws.amazon.com/dcv/latest/adminguide/setting-up-installing-linux-prereq.html#linux-prereq-wayland>`__
-3. `Driver
-   Installation <https://docs.aws.amazon.com/dcv/latest/adminguide/setting-up-installing-linux-prereq.html#linux-prereq-nongpu>`__
+4. `Driver
+   Installation and Setting Virtual Display Resolution <https://docs.aws.amazon.com/dcv/latest/adminguide/setting-up-installing-linux-prereq.html#linux-prereq-nongpu>`__
 
 Amazon DCV Server Installation
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -151,9 +152,6 @@ Checks <#post-installation-checks>`__ in the ``Password`` box and click
 At this point, you should see your session begin and a virtual desktop
 displayed after a brief delay.
 
-Any popups about not having a license may be safely ignored. This is a
-known issue with DCV.
-
 Now, open a terminal and run the following command:
 ``source /etc/profile.d/default_module.sh``. You're now ready to use
 your GUI-enabled EC2 Instance.
diff --git a/docs-rtd/source/sdk/README.rst b/docs-rtd/source/sdk/README.rst
index 4a3f32171..e7eb2461b 100644
--- a/docs-rtd/source/sdk/README.rst
+++ b/docs-rtd/source/sdk/README.rst
@@ -74,6 +74,8 @@ Additional SDK Documentation
 
    userspace/fpga_mgmt_examples/README
 
+   userspace/cython-bindings/README
+
    userspace/fpga_mgmt_tools/README
    docs/F2-Software-Performance-Optimization-Guide
    docs/Load-Times
diff --git a/docs-rtd/source/sdk/userspace/cython-bindings/README.rst b/docs-rtd/source/sdk/userspace/cython-bindings/README.rst
new file mode 100644
index 000000000..89cdeb46c
--- /dev/null
+++ b/docs-rtd/source/sdk/userspace/cython-bindings/README.rst
@@ -0,0 +1,136 @@
+Python Bindings
+===============
+
+These bindings exist to provide Python interfaces to the FPGA on AWS F2 
+EC2 Instances, allowing developers to control and interact with FPGAs 
+using Python instead of C code directly.
+
+Cython Overview
+---------------
+
+A typical Cython binding setup creates a bridge between Python and C
+code through a specific file structure: the ``.pxd`` file declares the
+external C functions and types (similar to a C header file), while the
+``.pyx`` file implements the actual Python-facing wrappers around these
+C functions, handling type conversions and memory management.
+
+When compiled, Cython transforms the ``.pyx`` file into C code, which is
+then built into a shared object (``.so``) file that Python can import
+directly as a module, allowing Python code to seamlessly call C
+functions while maintaining Python's ease of use but with C's
+performance benefits.
+
+Setup
+-----
+
+How to Build Bindings
+~~~~~~~~~~~~~~~~~~~~~
+
+.. code:: bash
+
+   git clone https://github.com/aws/aws-fpga.git
+   cd aws-fpga
+   source sdk_setup.sh
+
+This process will generate the necessary ``*_wrapper.c`` files that
+enable Python-to-C communication.
+
+Instructions to run examples
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Navigate to the ``aws-fpga/sdk/userspace/cython_bindings`` directory
+
+.. code:: bash
+
+   sudo python3 fpga_mgmt_example.py
+   sudo python3 fpga_clkgen_example.py
+   sudo python3 fpga_pci_example.py
+
+Troubleshooting
+---------------
+
+- FPGA Unresponsive: Run Python scripts with sudo privileges
+- Library not found: Verify AWS FPGA SDK installation is complete and
+  sourced
+- Invalid slot ID: Verify slot number is valid for instance type
+- AFI load timeout: Check AFI ID and instance permissions, and ensure
+  sufficient time after async FPGA clears and loads
+- Debug: Enable verbose logging by setting logging.INFO in ``utils.py``
+- Supported Python Versions: Bindings can be used by all Python versions
+  supported by Cython
+- How do I find my instance type during runtime: `Instance Meta Data
+  Documentation <https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html#instance-metadata-retrieval-examples>`__
+
+FPGA Management Library Functions
+---------------------------------
+
+These are the core functions that provide direct interaction with AWS F2
+FPGA instances. The primary functions include FPGA slot initialization,
+image loading/clearing, status checking, and metric gathering. These
+functions form the API layer between Python applications and the
+low-level FPGA hardware management, allowing developers to control FPGA
+resources without dealing directly with the hardware registers or
+low-level C interfaces.
+
+- ``load_local_image(self, slot_id: int, afi_id: str) -> dict``
+- ``clear_local_image(self, slot_id: int) -> dict``
+- ``describe_local_image(self, slot_id: int, flags: uint32_t) -> dict``
+- ``strerror(error: int) -> str``
+- ``strerror_long(err: int) -> str``
+- ``get_status_name(status: int) -> str``
+- ``get_status(self, slot_id: int) -> dict``
+- ``set_cmd_timeout(self, value: uint32_t) -> None``
+- ``set_cmd_delay_msec(self, value: uint32_t) -> None``
+- ``get_vLED_status(self, slot_id: int) -> uint16_t``
+- ``set_vDIP(self, slot_id: int, value: uint16_t) -> None``
+- ``get_vDIP_status(self, slot_id: int) -> uint16_t``
+- ``clear_local_image_sync(self, slot_id: int, timeout: uint32_t, delay_msec: uint32_t) -> dict``
+- ``load_local_image_flags(self, slot_id: int, afi_id: str, flags: uint32_t) -> dict``
+- ``load_local_image_sync_flags(self, slot_id: int, afi_id: str, flags: uint32_t, timeout: uint32_t, delay_msec: uint32_t) -> dict``
+
+FPGA Clock Generation Library Functions
+---------------------------------------
+
+The Clock Generation Library provide essential clock management
+capabilities for AWS FPGA instances, allowing precise control over clock
+frequencies and configurations. The primary functions include retrieving
+current clock settings, applying predefined clock recipes, and
+dynamically adjusting frequencies across multiple clock domains (A, B,
+C, and HBM). These functions form the API layer between Python
+applications and the low-level clock management system, allowing
+developers to precisely control FPGA clock resources without directly
+manipulating hardware registers. More information on clock generation
+functions are available in the
+`Clock Recipes User Guide <../../../hdk/docs/Clock-Recipes-User-Guide.html>`__
+document.
+
+- ``get_dynamic(self, slot_id: int) -> str``
+- ``set_recipe(self, slot_id: int, clk_a_recipe: uint32_t, clk_b_recipe: uint32_t, clk_c_recipe: uint32_t, clk_hbm_recipe: uint32_t, reset: uint32_t) -> None``
+- ``set_dynamic(self, slot_id: int, clk_a_freq: uint32_t, clk_b_freq: uint32_t, clk_c_freq: uint32_t, clk_hbm_freq: uint32_t, reset: uint32_t) -> None``
+
+FPGA PCI Library Functions
+--------------------------
+
+The FPGA PCI library provides a comprehensive set of functions for
+managing and interacting with the PCI bus on AWS FPGA instances. The
+library starts with initialization to set up the PCI management
+interface, and ``pci_attach()``/``pci_detach()`` to establish and
+terminate connections to specific PCI Base Address Registers (BARs).
+These functions form the basis for accessing PCI-mapped hardware
+resources.
+
+- ``pci_attach(self, slot_id: int, pf_id: int, bar_id: int, flags: uint32_t) -> pci_bar_handle_t``
+- ``pci_detach(self, handle: pci_bar_handle_t) -> None``
+- ``pci_poke(self, handle: pci_bar_handle_t, offset: uint64_t, value: uint32_t) -> None``
+- ``pci_poke8(self, handle: pci_bar_handle_t, offset: uint64_t, value: uint8_t) -> None``
+- ``pci_poke64(self, handle: pci_bar_handle_t, offset: uint64_t, value: uint64_t) -> None``
+- ``pci_write_burst(self, handle: pci_bar_handle_t, offset: uint64_t, data: List[int], dword_len: uint64_t) -> None``
+- ``pci_peek(self, handle: pci_bar_handle_t, offset: uint64_t) -> uint32_t``
+- ``pci_peek8(self, handle: pci_bar_handle_t, offset: uint64_t) -> uint8_t``
+- ``pci_peek64(self, handle: pci_bar_handle_t, offset: uint64_t) -> uint64_t``
+- ``pci_get_slot_spec(self, slot_id: int) -> fpga_slot_spec``
+- ``pci_get_all_slot_specs(self, size: int) -> List[fpga_slot_spec]``
+- ``pci_get_resource_map(self, slot_id: int, pf_id: int) -> fpga_pci_resource_map``
+- ``pci_rescan_slot_app_pfs(self, slot_id: int) -> None``
+- ``pci_get_address(self, handle: pci_bar_handle_t, offset: uint64_t, dword_len: uint64_t) -> uintptr_t``
+- ``pci_memset(self, handle: pci_bar_handle_t, offset: uint64_t, value: uint32_t, dword_len: uint64_t) -> None``
diff --git a/docs-rtd/source/sitemap.xml b/docs-rtd/source/sitemap.xml
index bf6157b3d..3270c4063 100644
--- a/docs-rtd/source/sitemap.xml
+++ b/docs-rtd/source/sitemap.xml
@@ -2,189 +2,194 @@
 <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
     <url>
         <loc>https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/ERRATA.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/RELEASE-NOTES.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>
             https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/User-Guide-AWS-EC2-FPGA-Development-Kit.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/all-links.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/developer-resources/Amazon-DCV-Setup-Guide.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/hdk/README.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>
             https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/hdk/cl/CHECKLIST-BEFORE-BUILDING-CL.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>
             https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/hdk/cl/examples/CL-TEMPLATE/README.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>
             https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/hdk/cl/examples/cl-dram-hbm-dma/README.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>
             https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/hdk/cl/examples/cl-dram-hbm-dma/verif/README.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>
             https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/hdk/cl/examples/cl-mem-perf/README.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>
             https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/hdk/cl/examples/cl-mem-perf/verif/README.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>
             https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/hdk/cl/examples/cl-sde/README.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>
             https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/hdk/cl/examples/cl-sde/software/src/README.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>
             https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/hdk/cl/examples/cl-sde/verif/README.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>
             https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/hdk/docs/AWS-CLI-FPGA-Commands.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/hdk/docs/AWS-CLK-GEN-spec.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>
             https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/hdk/docs/AWS-Fpga-Pcie-Memory-Map.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/hdk/docs/AWS-Shell-ERRATA.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>
             https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/hdk/docs/AWS-Shell-Interface-Specification.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>
             https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/hdk/docs/Clock-Recipes-User-Guide.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/hdk/docs/List-AFI-on-Marketplace.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>
             https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/hdk/docs/RTL-Simulation-Guide-for-HDK-Design-Flow.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>
             https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/hdk/docs/Supported-DDR-Modes.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/hdk/docs/Virtual-JTAG-XVC.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/hdk/docs/XDMA-Install.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>
             https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/hdk/docs/on-premise-licensing-help.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/hdk/docs/shell-floorplan.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/index.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/sdk/README.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>
             https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/sdk/apps/virtual-ethernet/README.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>
             https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/sdk/apps/virtual-ethernet/doc/SDE-HW-Guide.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>
             https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/sdk/apps/virtual-ethernet/doc/Virtual-Ethernet-Application-Guide.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>
             https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/sdk/docs/F2-Software-Performance-Optimization-Guide.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/sdk/docs/Load-Times.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
+    </url>
+    <url>
+        <loc>
+          https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/sdk/userspace/cython-bindings/README.html</loc>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>
             https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/sdk/userspace/fpga_mgmt_examples/README.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>
             https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/sdk/userspace/fpga_mgmt_tools/README.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/vitis/ERRATA.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/vitis/README.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/genindex.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
     <url>
         <loc>https://awsdocs-fpga-f2.readthedocs-hosted.com/latest/en/search.html</loc>
-        <lastmod>2025-06-26T00:00:01+00:00</lastmod>
+        <lastmod>2025-07-15T00:00:01+00:00</lastmod>
     </url>
 </urlset>
\ No newline at end of file
diff --git a/docs-rtd/source/spelling_wordlist.txt b/docs-rtd/source/spelling_wordlist.txt
index 4d7807aed..e72f943ac 100644
--- a/docs-rtd/source/spelling_wordlist.txt
+++ b/docs-rtd/source/spelling_wordlist.txt
@@ -270,6 +270,9 @@ MiB
 GiB
 TiB
 uncomment
+Cython
+PCI
 github
 Startup
 popups
+morgnza
diff --git a/hdk/cl/examples/cl_mem_perf/verif/tests/cl_mem_perf_utils.svh b/hdk/cl/examples/cl_mem_perf/verif/tests/cl_mem_perf_utils.svh
index 5186e8690..925e1bd48 100644
--- a/hdk/cl/examples/cl_mem_perf/verif/tests/cl_mem_perf_utils.svh
+++ b/hdk/cl/examples/cl_mem_perf/verif/tests/cl_mem_perf_utils.svh
@@ -164,10 +164,13 @@ task print_cl_hbm_perf_kernel_bandwidth_performance(logic [31:0] selected_channe
    logic [63:0] wr_cyc_count, wr_timer;
    logic [63:0] rd_cyc_count, rd_timer;
 
+   $display("Reading selected channels %d", $countones(selected_channels));
+   $display("Reading axlen %x", axlen);
+
    $display("Reading WR_CYC_CNT");
    tb.peek_ocl(.addr(`WR_CYC_CNT_LO_REG), .data(wr_cyc_count[31:0]));
    tb.peek_ocl(.addr(`WR_CYC_CNT_HI_REG), .data(wr_cyc_count[63:32]));
-   $display("WR_CYC_CNT value = 0x%x", wr_cyc_count);
+   $display("WR_CYC_CNT value = %d", wr_cyc_count);
 
    $display("Reading WR_TIMER");
    tb.peek_ocl(.addr(`WR_TIMER_LO_REG), .data(wr_timer[31:0]));
@@ -177,7 +180,7 @@ task print_cl_hbm_perf_kernel_bandwidth_performance(logic [31:0] selected_channe
    $display("Reading RD_CYC_CNT");
    tb.peek_ocl(.addr(`RD_CYC_CNT_LO_REG), .data(rd_cyc_count[31:0]));
    tb.peek_ocl(.addr(`RD_CYC_CNT_HI_REG), .data(rd_cyc_count[63:32]));
-   $display("RD_CYC_CNT value = 0x%x", rd_cyc_count);
+   $display("RD_CYC_CNT value = %d", rd_cyc_count);
 
    $display("Reading RD_TIMER");
    tb.peek_ocl(.addr(`RD_TIMER_LO_REG), .data(rd_timer[31:0]));
@@ -200,8 +203,11 @@ task print_cl_hbm_perf_kernel_bandwidth_performance(logic [31:0] selected_channe
    // Adjust by number of used channels
    // Adjust by axlen , wraddr overhead cycles
    // 0.90 toleraance
-   expected_wr_bandwidth = 0.90 * (450.0 * (axlen/ (axlen+1)) * ($countones(selected_channels) / 32.0));
-   expected_rd_bandwidth = 0.90 * (350.0 * ($countones(selected_channels) / 32.0));
+   expected_wr_bandwidth = 0.95 * (450.0 * (axlen+1)/(axlen+2)) * ($countones(selected_channels) / 32.0);
+   expected_rd_bandwidth = 0.95 * (350.0 * (axlen+1)/(axlen+2)) * ($countones(selected_channels) / 32.0);
+
+   $display("Exp Write BW = %-0.2f GB/s", expected_wr_bandwidth);
+   $display("Exp Read BW  = %-0.2f GB/s", expected_rd_bandwidth);
 
    if (wr_bw < expected_wr_bandwidth) begin
       $error("Write Bandwidth of %3.1f is below %3.1f GB/s", wr_bw, expected_wr_bandwidth);
diff --git a/release_version.txt b/release_version.txt
index a3fb64d66..464e6bc32 100644
--- a/release_version.txt
+++ b/release_version.txt
@@ -1 +1 @@
-RELEASE_VERSION=2.1.1
+RELEASE_VERSION=2.1.2
diff --git a/sdk/README.md b/sdk/README.md
index e574afe2c..893c7e634 100644
--- a/sdk/README.md
+++ b/sdk/README.md
@@ -32,3 +32,22 @@ Early release of the AFI management tools may return uninformative errors or une
 The `fpga-describe-local-image` API is currently asynchronous which will require polling with `fpga-describe-local-image` until the expected image appears. If the describe call does not provide the expected response, attempt the `fpga-load-local-image` one more time. Attempting to load images that are not compatible with the currently loaded shell will fail and may not return an informative error message. Please verify the design was built with the shell that is loaded on the instance.
 
 Please reach out to the AWS FPGA team with any instability issues so we can help as soon as possible.
+
+## Additional SDK Documentation
+
+* [Virtual Ethernet](./apps/virtual-ethernet/README.md)
+
+* [Virtual Ethernet SDE HW Guide](./apps/virtual-ethernet/doc/SDE_HW_Guide.md)
+
+* [Virtual Ethernet Application Guide](./apps/virtual-ethernet/doc/Virtual_Ethernet_Application_Guide.md)
+
+* [FPGA Management Examples](./userspace/fpga_mgmt_examples/README.md)
+
+* [Python Bindings](./userspace/cython_bindings/README.md)
+
+* [FPGA Management Tools](./userspace/fpga_mgmt_tools/README.md)
+
+* [F2 Software Performance Optimization Guide](./docs/F2_Software_Performance_Optimization_Guide.md)
+
+* [Load-Times](./docs/Load_Times.md)
+
diff --git a/sdk/userspace/cython_bindings/README.md b/sdk/userspace/cython_bindings/README.md
new file mode 100644
index 000000000..546d04836
--- /dev/null
+++ b/sdk/userspace/cython_bindings/README.md
@@ -0,0 +1,93 @@
+# Python Bindings
+
+These bindings exist to provide Python interfaces to the FPGA on AWS F2 EC2 Instances,
+allowing developers to control and interact with FPGAs using Python instead of C code directly.
+
+## Cython Overview
+
+A typical Cython binding setup creates a bridge between Python and C code through a specific file structure: 
+the `.pxd` file declares the external C functions and types (similar to a C header file), 
+while the `.pyx` file implements the actual Python-facing wrappers around these C functions, 
+handling type conversions and memory management. 
+
+When compiled, Cython transforms the `.pyx` file into C code, which is then built into a shared object (`.so`) file that Python can import directly as a module, allowing Python code to seamlessly call C functions while maintaining Python's ease of use but with C's performance benefits.
+
+## Setup 
+
+### How to Build Bindings
+
+```bash
+git clone https://github.com/aws/aws-fpga.git
+cd aws-fpga
+source sdk_setup.sh
+```
+
+This process will generate the necessary `*_wrapper.c` files that enable Python-to-C communication.
+
+### Instructions to run examples
+
+Navigate to the `aws-fpga/sdk/userspace/cython_bindings` directory
+
+```bash
+sudo python3 fpga_mgmt_example.py
+sudo python3 fpga_clkgen_example.py
+sudo python3 fpga_pci_example.py
+```
+
+## Troubleshooting
+
+- FPGA Unresponsive: Run Python scripts with sudo privileges
+- Library not found: Verify AWS FPGA SDK installation is complete and sourced
+- Invalid slot ID: Verify slot number is valid for instance type
+- AFI load timeout: Check AFI ID and instance permissions, and ensure sufficient time after async FPGA clears and loads
+- Debug: Enable verbose logging by setting logging.INFO in `utils.py`
+- Supported Python Versions: Bindings can be used by all Python versions supported by Cython
+- How do I find my instance type during runtime: [Instance Meta Data Documentation](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html#instance-metadata-retrieval-examples)
+
+## FPGA Management Library Functions
+
+These are the core functions that provide direct interaction with AWS F2 FPGA instances. The primary functions include FPGA slot initialization, image loading/clearing, status checking, and metric gathering. These functions form the API layer between Python applications and the low-level FPGA hardware management, allowing developers to control FPGA resources without dealing directly with the hardware registers or low-level C interfaces.
+
+- `load_local_image(self, slot_id: int, afi_id: str) -> dict`
+- `clear_local_image(self, slot_id: int) -> dict`
+- `describe_local_image(self, slot_id: int, flags: uint32_t) -> dict`
+- `strerror(error: int) -> str`
+- `strerror_long(err: int) -> str`
+- `get_status_name(status: int) -> str`
+- `get_status(self, slot_id: int) -> dict`
+- `set_cmd_timeout(self, value: uint32_t) -> None`
+- `set_cmd_delay_msec(self, value: uint32_t) -> None`
+- `get_vLED_status(self, slot_id: int) -> uint16_t`
+- `set_vDIP(self, slot_id: int, value: uint16_t) -> None`
+- `get_vDIP_status(self, slot_id: int) -> uint16_t`
+- `clear_local_image_sync(self, slot_id: int, timeout: uint32_t, delay_msec: uint32_t) -> dict`
+- `load_local_image_flags(self, slot_id: int, afi_id: str, flags: uint32_t) -> dict`
+- `load_local_image_sync_flags(self, slot_id: int, afi_id: str, flags: uint32_t, timeout: uint32_t, delay_msec: uint32_t) -> dict`
+
+## FPGA Clock Generation Library Functions
+
+The Clock Generation Library provide essential clock management capabilities for AWS FPGA instances, allowing precise control over clock frequencies and configurations. The primary functions include retrieving current clock settings, applying predefined clock recipes, and dynamically adjusting frequencies across multiple clock domains (A, B, C, and HBM). These functions form the API layer between Python applications and the low-level clock management system, allowing developers to precisely control FPGA clock resources without directly manipulating hardware registers. More information on clock generation functions are available in the [Clock Recipes User Guide](https://github.com/aws/aws-fpga/blob/f2/hdk/docs/Clock_Recipes_User_Guide.md) document.
+
+- `get_dynamic(self, slot_id: int) -> str`
+- `set_recipe(self, slot_id: int, clk_a_recipe: uint32_t, clk_b_recipe: uint32_t, clk_c_recipe: uint32_t, clk_hbm_recipe: uint32_t, reset: uint32_t) -> None`
+- `set_dynamic(self, slot_id: int, clk_a_freq: uint32_t, clk_b_freq: uint32_t, clk_c_freq: uint32_t, clk_hbm_freq: uint32_t, reset: uint32_t) -> None`
+
+## FPGA PCI Library Functions
+
+The FPGA PCI library provides a comprehensive set of functions for managing and interacting with the PCI bus on AWS FPGA instances. The library starts with initialization to set up the PCI management interface, and `pci_attach()`/`pci_detach()` to establish and terminate connections to specific PCI Base Address Registers (BARs). These functions form the basis for accessing PCI-mapped hardware resources.
+
+- `pci_attach(self, slot_id: int, pf_id: int, bar_id: int, flags: uint32_t) -> pci_bar_handle_t`
+- `pci_detach(self, handle: pci_bar_handle_t) -> None`
+- `pci_poke(self, handle: pci_bar_handle_t, offset: uint64_t, value: uint32_t) -> None`
+- `pci_poke8(self, handle: pci_bar_handle_t, offset: uint64_t, value: uint8_t) -> None`
+- `pci_poke64(self, handle: pci_bar_handle_t, offset: uint64_t, value: uint64_t) -> None`
+- `pci_write_burst(self, handle: pci_bar_handle_t, offset: uint64_t, data: List[int], dword_len: uint64_t) -> None`
+- `pci_peek(self, handle: pci_bar_handle_t, offset: uint64_t) -> uint32_t`
+- `pci_peek8(self, handle: pci_bar_handle_t, offset: uint64_t) -> uint8_t`
+- `pci_peek64(self, handle: pci_bar_handle_t, offset: uint64_t) -> uint64_t`
+- `pci_get_slot_spec(self, slot_id: int) -> fpga_slot_spec`
+- `pci_get_all_slot_specs(self, size: int) -> List[fpga_slot_spec]`
+- `pci_get_resource_map(self, slot_id: int, pf_id: int) -> fpga_pci_resource_map`
+- `pci_rescan_slot_app_pfs(self, slot_id: int) -> None`
+- `pci_get_address(self, handle: pci_bar_handle_t, offset: uint64_t, dword_len: uint64_t) -> uintptr_t`
+- `pci_memset(self, handle: pci_bar_handle_t, offset: uint64_t, value: uint32_t, dword_len: uint64_t) -> None`
diff --git a/sdk/userspace/cython_bindings/fpga_clkgen.pxd b/sdk/userspace/cython_bindings/fpga_clkgen.pxd
new file mode 100644
index 000000000..0993a78f6
--- /dev/null
+++ b/sdk/userspace/cython_bindings/fpga_clkgen.pxd
@@ -0,0 +1,19 @@
+# Cython Header File for FPGA Clkgen Functions
+
+from libc.stdint cimport uint32_t
+
+# fpga_clkgen.pxd
+cdef extern from "fpga_clkgen.h":
+
+    struct fpga_clkgen_group:
+        double[3] clocks
+
+    struct fpga_clkgen_info:
+        fpga_clkgen_group clock_group_a
+        fpga_clkgen_group clock_group_b
+        fpga_clkgen_group clock_group_c
+        fpga_clkgen_group clock_group_hbm
+
+    int aws_clkgen_get_dynamic(int slot_id, fpga_clkgen_info* info)
+    int aws_clkgen_set_recipe(int slot_id, uint32_t clk_a_recipe, uint32_t clk_b_recipe, uint32_t clk_c_recipe, uint32_t clk_hbm_recipe, uint32_t reset);
+    int aws_clkgen_set_dynamic(int slot_id, uint32_t clk_a_freq, uint32_t clk_b_freq, uint32_t clk_c_freq, uint32_t clk_hbm_freq, uint32_t reset);
diff --git a/sdk/userspace/cython_bindings/fpga_clkgen_example.py b/sdk/userspace/cython_bindings/fpga_clkgen_example.py
new file mode 100644
index 000000000..56d47b4bc
--- /dev/null
+++ b/sdk/userspace/cython_bindings/fpga_clkgen_example.py
@@ -0,0 +1,89 @@
+"""
+ * Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"). You may
+ * not use this file except in compliance with the License. A copy of the
+ * License is located at
+ *
+ *     http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+
+* This example demonstrates how to manage FPGA clock settings using the C API, including:
+* - Reading FPGA status and clock information
+* - Loading an AFI (Amazon FPGA Image)
+* - Setting dynamic clock frequencies
+* - Applying clock recipes
+*
+* 0. Prerequisites: This example must be run on an F2 instance with an FPGA. Source the sdk
+*    by navigating to the root of this repo and running `source ./sdk_setup.sh`.
+* 1. The example initializes FPGA management and clock generation wrappers
+* 2. Checks FPGA status and loads a specified AFI
+* 3. Retrieves current clock information
+* 4. Demonstrates setting dynamic clock frequencies for different clock groups
+* 5. Shows how to apply a predefined clock recipe
+* 6. Verifies clock settings after modifications
+"""
+
+from fpga_clkgen_wrapper import FpgaClkgen
+from typing import Dict, Any
+from fpga_mgmt_wrapper import FpgaMgmt
+from utils import setup_logger
+import json
+
+
+def main() -> None:
+    setup_logger()
+
+    GET_HW_METRICS = 1 << 1
+
+    fpga_mgmt_wrapper = FpgaMgmt()
+    fpga_clkgen_wrapper = FpgaClkgen()
+
+    slot = 0
+    status: str = fpga_mgmt_wrapper.describe_local_image(slot, GET_HW_METRICS)["status"]
+    print(f"FPGA Status {status}")
+
+    public_cl_mem_perf_afi_id = "agfi-080817d089f3cd2ed"
+    print(f"Loading AFI: {public_cl_mem_perf_afi_id}\n")
+    image_info: Dict[str, Any] = fpga_mgmt_wrapper.load_local_image(
+        slot, public_cl_mem_perf_afi_id
+    )
+
+    status: str = image_info["status"]
+    while status == "busy":
+        status = fpga_mgmt_wrapper.describe_local_image(slot, GET_HW_METRICS)[
+            "status"
+        ]
+
+    info: Dict[str, Any] = json.dumps(fpga_clkgen_wrapper.get_dynamic(slot), indent=2)
+    print(f"Clock Information\n {info}")
+
+    print("Setting dynamic clock\n")
+    fpga_clkgen_wrapper.set_dynamic(
+        slot, clk_a_freq=125, clk_b_freq=125, clk_c_freq=150, clk_hbm_freq=125, reset=0
+    )
+
+    info = json.dumps(fpga_clkgen_wrapper.get_dynamic(slot), indent=2)
+    print(f"New Clock Information\n {info}\n")
+
+    print("Setting Recipe\n")
+    # Recipe information available at aws-fpga/hdk/docs/Clock_Recipes_User_Guide.md
+    fpga_clkgen_wrapper.set_recipe(
+        slot_id=0,
+        clk_a_recipe=0,
+        clk_b_recipe=3,
+        clk_c_recipe=3,
+        clk_hbm_recipe=4,
+        reset=0,
+    )
+
+    info = json.dumps(fpga_clkgen_wrapper.get_dynamic(slot), indent=2)
+    print(f"Clock Information after setting recipe\n {info}\n")
+
+
+if __name__ == "__main__":
+    main()
diff --git a/sdk/userspace/cython_bindings/fpga_clkgen_wrapper.pyx b/sdk/userspace/cython_bindings/fpga_clkgen_wrapper.pyx
new file mode 100644
index 000000000..4f483309b
--- /dev/null
+++ b/sdk/userspace/cython_bindings/fpga_clkgen_wrapper.pyx
@@ -0,0 +1,22 @@
+# Cython Code File for FPGA ClkGen library
+
+from fpga_clkgen cimport *
+from fpga_mgmt cimport *
+from fpga_utils import check_return_code
+from typing import Dict, Any
+
+
+class FpgaClkgen:
+    def get_dynamic(self, slot_id: int) -> Dict[str, Any]:
+        cdef fpga_clkgen_info info
+        ret = aws_clkgen_get_dynamic(slot_id, &info)
+        check_return_code(ret, "get dynamic", slot_id)
+        return info
+
+    def set_recipe(self, slot_id: int, clk_a_recipe: uint32_t, clk_b_recipe: uint32_t, clk_c_recipe: uint32_t, clk_hbm_recipe: uint32_t, reset: uint32_t) -> None:
+        ret = aws_clkgen_set_recipe(slot_id, clk_a_recipe, clk_b_recipe, clk_c_recipe, clk_hbm_recipe, reset)
+        check_return_code(ret, "set recipe", slot_id)
+
+    def set_dynamic(self, slot_id: int, clk_a_freq: uint32_t, clk_b_freq: uint32_t, clk_c_freq: uint32_t, clk_hbm_freq: uint32_t, reset: uint32_t) -> None:
+        ret = aws_clkgen_set_dynamic(slot_id, clk_a_freq, clk_b_freq, clk_c_freq, clk_hbm_freq, reset)
+        check_return_code(ret, "set dynamic", slot_id)
diff --git a/sdk/userspace/cython_bindings/fpga_common.pxd b/sdk/userspace/cython_bindings/fpga_common.pxd
new file mode 100644
index 000000000..4911f7918
--- /dev/null
+++ b/sdk/userspace/cython_bindings/fpga_common.pxd
@@ -0,0 +1,77 @@
+from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t
+from libcpp cimport bool
+
+cdef extern from "hal/fpga_common.h":
+    struct fpga_ddr_if_metrics_common:
+        uint64_t write_count
+        uint64_t read_count
+
+    struct fpga_clocks_common:
+        uint64_t[7] frequency
+
+    struct fpga_pci_resource_map:
+        uint16_t vendor_id
+        uint16_t device_id
+        uint16_t subsystem_device_id
+        uint16_t subsystem_vendor_id
+        uint16_t domain
+        uint8_t  bus
+        uint8_t  dev
+        uint8_t  func
+        bool[5]  resource_burstable
+        uint64_t[5] resource_size
+
+    struct f1_metrics_common:
+        uint32_t int_status
+        uint32_t pcim_axi_protocol_error_status
+        uint64_t dma_pcis_timeout_addr
+        uint32_t dma_pcis_timeout_count
+        uint64_t pcim_range_error_addr
+        uint32_t pcim_range_error_count
+        uint64_t pcim_axi_protocol_error_addr
+        uint32_t pcim_axi_protocol_error_count
+        uint8_t[12] reserved2
+        uint64_t ocl_slave_timeout_addr
+        uint32_t ocl_slave_timeout_count
+        uint64_t bar1_slave_timeout_addr
+        uint32_t bar1_slave_timeout_count
+        uint32_t sdacl_slave_timeout_addr
+        uint32_t sdacl_slave_timeout_count
+        uint32_t virtual_jtag_slave_timeout_addr
+        uint32_t virtual_jtag_slave_timeout_count
+        uint64_t pcim_write_count
+        uint64_t pcim_read_count
+        fpga_ddr_if_metrics_common[4] ddr_ifs
+        fpga_clocks_common[3] clocks
+        uint64_t power_mean
+        uint64_t power_max
+        uint64_t power
+        uint64_t[16] cached_agfis
+        uint64_t flags
+
+    struct f2_metrics_common:
+        uint32_t int_status
+        uint32_t pcim_axi_protocol_error_status
+        uint64_t pcim_range_error_addr
+        uint32_t pcim_range_error_count
+        uint64_t pcim_axi_protocol_error_addr
+        uint32_t pcim_axi_protocol_error_count
+        uint64_t pcim_write_count
+        uint64_t pcim_read_count
+        uint64_t dma_pcis_timeout_addr
+        uint32_t dma_pcis_timeout_count
+        uint32_t ocl_slave_timeout_addr
+        uint32_t ocl_slave_timeout_count
+        uint64_t sda_slave_timeout_addr
+        uint32_t sda_slave_timeout_count
+        uint32_t virtual_jtag_slave_timeout_addr
+        uint32_t virtual_jtag_slave_timeout_count
+        uint32_t virtual_jtag_write_count
+        uint32_t virtual_jtag_read_count
+        fpga_ddr_if_metrics_common[1] ddr_ifs
+        fpga_clocks_common[3] clocks
+        uint64_t power_mean
+        uint64_t power_max
+        uint64_t power
+        uint64_t[16] cached_agfis
+        uint64_t flags
diff --git a/sdk/userspace/cython_bindings/fpga_mgmt.pxd b/sdk/userspace/cython_bindings/fpga_mgmt.pxd
new file mode 100644
index 000000000..0e42ccb2a
--- /dev/null
+++ b/sdk/userspace/cython_bindings/fpga_mgmt.pxd
@@ -0,0 +1,66 @@
+# Cython Header File for FPGA Management Functions
+
+from libc.stdint cimport uint8_t, uint16_t, uint32_t
+from fpga_common cimport f1_metrics_common, f2_metrics_common, fpga_pci_resource_map
+
+# fpga_mgmt.pxd
+cdef extern from "fpga_mgmt.h":
+
+    struct fpga_meta_ids:
+        char[64] afi_id
+
+    struct fpga_slot_spec:
+        fpga_pci_resource_map[2] map
+
+    struct metrics:
+        f1_metrics_common f1_metrics
+        f2_metrics_common f2_metrics
+
+    struct fpga_mgmt_image_info:
+        int status
+        int status_q
+        int slot_id
+        fpga_meta_ids ids
+        fpga_slot_spec spec
+        uint32_t sh_version
+        metrics metrics
+
+    struct options:
+        int slot_id
+        char[64] afi_id
+        uint32_t flags
+        uint32_t[3] clock_mains
+
+    union fpga_mgmt_load_local_image_options:
+        uint8_t[1024] reserved
+        options opt
+
+    ctypedef fpga_mgmt_load_local_image_options fpga_mgmt_load_local_image_options_t
+
+    int fpga_mgmt_init()
+    int fpga_mgmt_close()
+
+    const char *fpga_mgmt_strerror(int err)
+    const char *fpga_mgmt_strerror_long(int err)
+
+    void fpga_mgmt_set_cmd_timeout(uint32_t value)
+    void fpga_mgmt_set_cmd_delay_msec(uint32_t value)
+
+    int fpga_mgmt_describe_local_image(int slot_id, fpga_mgmt_image_info *info, uint32_t flags)
+
+    int fpga_mgmt_get_status(int slot_id, int *status, int *status_q)
+    const char *fpga_mgmt_get_status_name(int status)
+
+    int fpga_mgmt_clear_local_image(int slot_id)
+    int fpga_mgmt_clear_local_image_sync(int slot_id,
+        uint32_t timeout, uint32_t delay_msec, fpga_mgmt_image_info *info)
+
+    int fpga_mgmt_load_local_image(int slot_id, char *afi_id)
+    int fpga_mgmt_load_local_image_flags(int slot_id, char *afi_id, uint32_t flags)
+
+    int fpga_mgmt_load_local_image_sync_flags(int slot_id, char *afi_id, uint32_t flags,
+        uint32_t timeout, uint32_t delay_msec, fpga_mgmt_image_info *info)
+
+    int fpga_mgmt_get_vLED_status(int slot_id, uint16_t *status)
+    int fpga_mgmt_set_vDIP(int slot_id, uint16_t value)
+    int fpga_mgmt_get_vDIP_status(int slot_id, uint16_t *value)
diff --git a/sdk/userspace/cython_bindings/fpga_mgmt_example.py b/sdk/userspace/cython_bindings/fpga_mgmt_example.py
new file mode 100644
index 000000000..e07e6c734
--- /dev/null
+++ b/sdk/userspace/cython_bindings/fpga_mgmt_example.py
@@ -0,0 +1,96 @@
+"""
+ * Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"). You may
+ * not use this file except in compliance with the License. A copy of the
+ * License is located at
+ *
+ *     http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+
+ * This example demonstrates various FPGA management operations using the C API, including:
+ * - Clearing FPGA image slots
+ * - Loading and managing Amazon FPGA Images (AFIs)
+ * - Setting and reading virtual DIP switches
+ * - Handling AFI caches
+ *
+ * 0. Prerequisites: This example must be run on an F2 instance with an FPGA. Source the sdk
+ *    by navigating to the root of this repo and running `source ./sdk_setup.sh`.
+ * 1. The example initializes the FPGA management wrapper
+ * 2. Demonstrates clearing a local image from an FPGA slot
+ * 3. Loads a specific AFI (Streaming Data Engine CL Example)
+ * 4. Shows both synchronous and asynchronous clearing of local images
+ * 5. Demonstrates setting and reading virtual DIP switches
+ * 6. Illustrates the use of the CLEAR_AFI_CACHE flag
+ * 7. Performs synchronous loading of a local image
+ * 8. Uses describe_local_image to verify and display FPGA status and metrics
+"""
+
+from fpga_mgmt_wrapper import FpgaMgmt
+from utils import setup_logger, convert_info_to_json
+from typing import Dict, Any
+
+
+def main() -> None:
+    setup_logger()
+
+    GET_HW_METRICS = 1 << 1
+    CLEAR_AFI_CACHE = 1 << 8
+
+    fpga_mgmt_wrapper = FpgaMgmt()
+
+    slot = 0
+    image_info: Dict[str, Any] = fpga_mgmt_wrapper.clear_local_image(slot)
+
+    status: str = image_info["status"]
+    while status == "busy":
+        status = fpga_mgmt_wrapper.describe_local_image(slot, GET_HW_METRICS)[
+            "status"
+        ]
+
+    public_cl_sde_afi_id = "agfi-0925b211f5a81b071"
+    print(
+        f"Loading AFI for SDE (Streaming Data Engine) CL Example: {public_cl_sde_afi_id}\n"
+    )
+
+    fpga_mgmt_wrapper.load_local_image(slot, public_cl_sde_afi_id)
+    fpga_mgmt_wrapper.set_cmd_delay_msec(value=100000)
+
+    info: Dict[str, Any] = fpga_mgmt_wrapper.describe_local_image(slot, GET_HW_METRICS)
+    print(f"Info {convert_info_to_json(info)}\n")
+
+    print("Clearing local image synchronously\n")
+    fpga_mgmt_wrapper.clear_local_image_sync(slot, timeout=60000, delay_msec=2)
+
+    vDIP_value = 1 << 5
+    print(f"Setting the dip switch at {vDIP_value}")
+    fpga_mgmt_wrapper.set_vDIP(slot, vDIP_value)
+
+    vDIP_status: int = fpga_mgmt_wrapper.get_vDIP_status(slot)
+    print(f"Dip switch state: {vDIP_status}\n")
+
+    print("Describing image before CLEAR_AFI_CACHE flag is set")
+    info = fpga_mgmt_wrapper.describe_local_image(slot, GET_HW_METRICS)
+    print(f"Info {info['metrics']['cached_agfis']}")
+
+    info = fpga_mgmt_wrapper.describe_local_image(slot, CLEAR_AFI_CACHE)
+    print(f"Info {info['metrics']['cached_agfis']}\n")
+
+    print("Clearing local image synchronously\n")
+    fpga_mgmt_wrapper.clear_local_image_sync(slot, timeout=60000, delay_msec=2)
+
+    print("Loading local image synchronously\n")
+    fpga_mgmt_wrapper.load_local_image_sync_flags(
+        slot, public_cl_sde_afi_id, GET_HW_METRICS, timeout=60000, delay_msec=2
+    )
+
+    info = fpga_mgmt_wrapper.describe_local_image(slot, GET_HW_METRICS)
+    print(f"Info {convert_info_to_json(info)}")
+
+
+if __name__ == "__main__":
+    main()
diff --git a/sdk/userspace/cython_bindings/fpga_mgmt_wrapper.pyx b/sdk/userspace/cython_bindings/fpga_mgmt_wrapper.pyx
new file mode 100644
index 000000000..3eb9e6f22
--- /dev/null
+++ b/sdk/userspace/cython_bindings/fpga_mgmt_wrapper.pyx
@@ -0,0 +1,119 @@
+# Cython Code File for FPGA Management library
+
+from fpga_mgmt cimport *
+from libc.stdint cimport uint16_t, uint32_t
+import logging
+from fpga_utils import check_return_code
+from typing import Dict, List, Any
+
+class FpgaMgmt:
+    def __init__(self) -> None:
+        init_status = fpga_mgmt_init()
+        check_return_code(init_status, "initialize FPGA Management Library", -1)
+
+    def __del__(self) -> None:
+        logging.info("Closing FPGA Management Library")
+        fpga_mgmt_close()
+
+    def load_local_image(self, slot_id : int, afi_id: str) -> Dict[str, Any]:
+        cdef bytes afi_id_bytes = afi_id.encode('utf-8')
+        ret = fpga_mgmt_load_local_image(slot_id, afi_id_bytes)
+        check_return_code(ret, "load AFI", slot_id)
+        return FpgaMgmt.describe_local_image(self, slot_id, 1<<1)
+
+    def clear_local_image(self, slot_id: int) -> Dict[str, Any]:
+        ret = fpga_mgmt_clear_local_image(slot_id)
+        check_return_code(ret, "clear image on FPGA", slot_id)
+        return FpgaMgmt.describe_local_image(self, slot_id, 1<<1)
+
+    @staticmethod
+    def process_cached_agfis(result: dict) -> List[str]:
+        formatted_agfis = []
+        for agfi_value in result['metrics']['cached_agfis']:
+            if agfi_value == 0:
+                formatted_agfis.append('0')
+            else:
+                formatted_agfis.append('agfi-' + hex(agfi_value)[2:])
+        return formatted_agfis
+
+    def describe_local_image(self, slot_id: int, flags: uint32_t) -> Dict[str, Any]:
+        cdef fpga_mgmt_image_info info
+
+        ret = fpga_mgmt_describe_local_image(slot_id, &info, flags)
+        check_return_code(ret, "describe local image", slot_id)
+
+        result = {
+            'status': FpgaMgmt.get_status_name(info.status),
+            'status_q': info.status_q,
+            'slot_id': info.slot_id,
+            'afi_id': info.ids,
+            'spec': info.spec,
+            'sh_version': info.sh_version,
+            'metrics': info.metrics.f2_metrics,
+        }
+
+        result['metrics']['cached_agfis'] = FpgaMgmt.process_cached_agfis(result)
+        return result
+
+    @staticmethod
+    def strerror(error: int) -> str:
+        val = fpga_mgmt_strerror(error)
+        return val.decode('utf-8')
+
+    @staticmethod
+    def strerror_long(err: int) -> str:
+        val = fpga_mgmt_strerror_long(err)
+        return val.decode('utf-8')
+
+    @staticmethod
+    def get_status_name(status: int) -> str:
+        return fpga_mgmt_get_status_name(status).decode('utf-8')
+
+    def get_status(self, slot_id: int) -> Dict[str, Any]:
+        cdef int status = 0
+        cdef int status_q = 0
+        ret = fpga_mgmt_get_status(slot_id, &status, &status_q)
+        check_return_code(ret, "get FPGA status", slot_id)
+        return {'status': status, 'status_q': status_q, 'return_code': ret}
+
+    def set_cmd_timeout(self, value: uint32_t) -> None:
+        fpga_mgmt_set_cmd_timeout(value)
+
+    def set_cmd_delay_msec(self, value: uint32_t) -> None:
+        fpga_mgmt_set_cmd_delay_msec(value)
+
+    def get_vLED_status(self, slot_id: int) -> uint16_t:
+        cdef uint16_t status
+        ret = fpga_mgmt_get_vLED_status(slot_id, &status)
+        check_return_code(ret, "get vLED status", slot_id)
+        return status
+
+    def set_vDIP(self, slot_id: int, value: uint16_t) -> None:
+        ret = fpga_mgmt_set_vDIP(slot_id, value)
+        check_return_code(ret, "set vDIP status", slot_id)
+
+    def get_vDIP_status(self, slot_id: int) -> uint16_t:
+        cdef uint16_t value
+        ret = fpga_mgmt_get_vDIP_status(slot_id, &value)
+        check_return_code(ret, "get vDIP status", slot_id)
+        return value
+
+    def clear_local_image_sync(self, slot_id: int, timeout: uint32_t, delay_msec: uint32_t) -> Dict[str, Any]:
+        cdef fpga_mgmt_image_info info
+        ret = fpga_mgmt_clear_local_image_sync(slot_id, timeout, delay_msec, &info);
+        check_return_code(ret, "clear local image sync", slot_id)
+        return FpgaMgmt.describe_local_image(self, slot_id, 1<<1)
+
+    def load_local_image_flags(self, slot_id: int, afi_id: str, flags: uint32_t) -> Dict[str, Any]:
+        cdef bytes afi_id_bytes = afi_id.encode('utf-8')
+        ret = fpga_mgmt_load_local_image_flags( slot_id, afi_id_bytes,  flags);
+        check_return_code(ret, "load AFI flags", slot_id)
+        return FpgaMgmt.describe_local_image(self, slot_id, 1<<1)
+
+    def load_local_image_sync_flags(self, slot_id: int, afi_id: str, flags: uint32_t, timeout: uint32_t, delay_msec: uint32_t) -> Dict[str, Any]:
+        cdef fpga_mgmt_image_info info
+        cdef bytes afi_id_bytes = afi_id.encode('utf-8')
+        ret = fpga_mgmt_load_local_image_sync_flags(slot_id, afi_id_bytes, flags, timeout, delay_msec, &info)
+        check_return_code(ret, "load AFI sync flags", slot_id)
+        return FpgaMgmt.describe_local_image(self, slot_id, 1<<1)
+
diff --git a/sdk/userspace/cython_bindings/fpga_pci.pxd b/sdk/userspace/cython_bindings/fpga_pci.pxd
new file mode 100644
index 000000000..39393e62f
--- /dev/null
+++ b/sdk/userspace/cython_bindings/fpga_pci.pxd
@@ -0,0 +1,46 @@
+# Cython Header File for FPGA PCI Functions
+
+from libc.stdint cimport uint8_t, uint32_t, uint64_t
+from fpga_common cimport fpga_pci_resource_map
+
+# fpga_pci.pxd
+cdef extern from "fpga_pci.h":
+    ctypedef int pci_bar_handle_t
+
+    struct fpga_slot_spec:
+        fpga_pci_resource_map[2] map
+
+    int fpga_pci_init()
+
+    int fpga_pci_attach(int slot_id, int pf_id, int bar_id, uint32_t flags,
+        pci_bar_handle_t *handle)
+
+    int fpga_pci_detach(pci_bar_handle_t handle)
+
+    int fpga_pci_poke(pci_bar_handle_t handle, uint64_t offset, uint32_t value)
+    int fpga_pci_poke8(pci_bar_handle_t handle, uint64_t offset, uint8_t value)
+
+    int fpga_pci_poke64(pci_bar_handle_t handle, uint64_t offset, uint64_t value)
+
+    int fpga_pci_write_burst(pci_bar_handle_t handle, uint64_t offset,
+    uint32_t* datap, uint64_t dword_len)
+
+    int fpga_pci_peek(pci_bar_handle_t handle, uint64_t offset, uint32_t *value)
+
+    int fpga_pci_peek8(pci_bar_handle_t handle, uint64_t offset, uint8_t *value)
+
+    int fpga_pci_peek64(pci_bar_handle_t handle, uint64_t offset, uint64_t *value)
+
+    int fpga_pci_get_slot_spec(int slot_id, fpga_slot_spec *spec)
+
+    int fpga_pci_get_all_slot_specs(fpga_slot_spec[] spec_array, int size)
+
+    int fpga_pci_get_resource_map(int slot_id, int pf_id, fpga_pci_resource_map *map)
+
+    int fpga_pci_rescan_slot_app_pfs(int slot_id)
+
+    int fpga_pci_get_address(pci_bar_handle_t handle, uint64_t offset,
+	uint64_t dword_len, void **ptr)
+
+    int fpga_pci_memset(pci_bar_handle_t handle, uint64_t offset, uint32_t value,
+        uint64_t dword_len)
diff --git a/sdk/userspace/cython_bindings/fpga_pci_example.py b/sdk/userspace/cython_bindings/fpga_pci_example.py
new file mode 100644
index 000000000..0e5caa55a
--- /dev/null
+++ b/sdk/userspace/cython_bindings/fpga_pci_example.py
@@ -0,0 +1,127 @@
+"""
+ * Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"). You may
+ * not use this file except in compliance with the License. A copy of the
+ * License is located at
+ *
+ *     http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+
+ * This example demonstrates how to measure and monitor FPGA clock frequencies using the C API, including:
+ * - PCI operations for reading clock frequency counters
+ * - Monitoring multiple clock domains
+ * - Resource mapping
+ *
+ * 0. Prerequisites: This example must be run on an F2 instance with an FPGA. Source the sdk
+ *    by navigating to the root of this repo and running `source ./sdk_setup.sh`.
+ * 1. Initialize FPGA management and PCI wrappers
+ * 2. Load a specific memory performance AFI
+ * 3. Attach to PCI device and access control registers
+ * 4. Reset and trigger frequency measurements
+ * 5. Read frequency counters for multiple clock domains:
+ *    - Main clock (clk_main_a0)
+ *    - Extra clocks (a1-a3, b0-b1, c0-c1)
+ *    - HBM clocks (axi and ref)
+ * 6. Display measured frequencies and resource mapping
+ * 7. Clean up PCI resources
+"""
+
+from fpga_pci_wrapper import FpgaPCI
+from fpga_mgmt_wrapper import FpgaMgmt
+import time, json
+from typing import Dict, Any
+from utils import setup_logger, convert_info_to_json
+
+
+def read_freq_counters(handle: int, base_addr: int) -> Dict[str, float]:
+    fpga_pci_wrapper = FpgaPCI()
+    freq_counters: Dict[str, float] = {}
+    clk_names = [
+        "clk_main_a0",
+        "clk_extra_a1",
+        "clk_extra_a2",
+        "clk_extra_a3",
+        "clk_extra_b0",
+        "clk_extra_b1",
+        "clk_extra_c0",
+        "clk_extra_c1",
+        "clk_hbm_axi",
+        "clk_hbm_ref",
+    ]
+
+    for i, name in enumerate(clk_names):
+        addr = base_addr + 0x10 + (i * 4)  # FREQ_CTR_0 starts at base + 0x10
+        value = fpga_pci_wrapper.pci_peek(handle, addr)
+        freq_mhz = value / 1000000.0
+        freq_counters[name] = freq_mhz
+
+    return freq_counters
+
+
+def main() -> None:
+    setup_logger()
+
+    GET_HW_METRICS = 1 << 1
+
+    fpga_mgmt_wrapper = FpgaMgmt()
+    fpga_pci_wrapper = FpgaPCI()
+
+    slot_id = 0
+
+    public_cl_mem_perf_afi_id = "agfi-080817d089f3cd2ed"
+    print(f"Loading AFI for Mem Perf CL Example:  {public_cl_mem_perf_afi_id}\n")
+    image_info: Dict[str, Any] = fpga_mgmt_wrapper.load_local_image(slot_id, public_cl_mem_perf_afi_id)
+    status: str = image_info["status"]
+    while status == "busy":
+        status = fpga_mgmt_wrapper.describe_local_image(slot_id, GET_HW_METRICS)[
+            "status"
+        ]
+
+    info: Dict[str, Any] = fpga_mgmt_wrapper.describe_local_image(slot_id, GET_HW_METRICS)
+    print(f"Info {convert_info_to_json(info)}\n")
+
+    pf_id = 0
+    bar_id = 0
+    fpga_attach_flags = 0
+    addr = 0x600  # CTL_REG
+
+    handle = fpga_pci_wrapper.pci_attach(slot_id, pf_id, bar_id, fpga_attach_flags)
+
+    # Reset frequency counters
+    rc = fpga_pci_wrapper.pci_poke(handle, addr, value=0x80000000)
+    rc = fpga_pci_wrapper.pci_poke(handle, addr, value=0x0)
+
+    # Trigger measurement
+    rc = fpga_pci_wrapper.pci_poke(handle, addr, value=0x1)
+
+    # Wait for measurement to complete
+    for _ in range(10):
+        time.sleep(1)
+        value = fpga_pci_wrapper.pci_peek(handle, addr)
+        if value == 0:  # Measurement complete
+            break
+
+    # Read the frequency counters
+    base_addr = 0x600
+    ref_freq: int = fpga_pci_wrapper.pci_peek(handle, base_addr + 0x04)
+    print(f"Reference Frequency: {ref_freq} Hz")
+
+    freq_counters = read_freq_counters(handle, base_addr)
+    print("\nClock Frequencies:")
+    for name, freq in freq_counters.items():
+        print(f"{name}: {freq:.4f} MHz")
+
+    fpga_pci_wrapper.pci_detach(handle)
+
+    print("\nResource Map")
+    map: Dict[str, Any] = json.dumps(fpga_pci_wrapper.pci_get_resource_map(slot_id, pf_id=0), indent=2)
+    print(f"{map}\n")
+
+
+if __name__ == "__main__":
+    main()
diff --git a/sdk/userspace/cython_bindings/fpga_pci_wrapper.pyx b/sdk/userspace/cython_bindings/fpga_pci_wrapper.pyx
new file mode 100644
index 000000000..6490d1bcd
--- /dev/null
+++ b/sdk/userspace/cython_bindings/fpga_pci_wrapper.pyx
@@ -0,0 +1,102 @@
+# Cython Code File for FPGA PCI library
+
+from fpga_pci cimport *
+from fpga_mgmt cimport *
+from libc.stdint cimport uint8_t, uint32_t, uint64_t, uintptr_t
+from fpga_utils import check_return_code
+from typing import List
+from libc.stdlib cimport malloc, free
+
+class FpgaPCI:
+    def __init__(self) -> None:
+        ret = fpga_pci_init()
+        check_return_code(ret, "initialize PCI Library", -1)
+
+    def pci_attach(self, slot_id: int, pf_id: int, bar_id: int, flags: uint32_t) -> pci_bar_handle_t:
+        cdef pci_bar_handle_t handle = -1
+        ret = fpga_pci_attach(slot_id, pf_id, bar_id, flags, &handle)
+        check_return_code(ret, "pci attach", slot_id)
+        return handle
+
+    def pci_detach(self, handle: pci_bar_handle_t) -> None:
+        ret = fpga_pci_detach(handle)
+        check_return_code(ret, "pci detach", -1)
+
+    def pci_poke(self, handle: pci_bar_handle_t, offset: uint64_t, value: uint32_t) -> None:
+        ret = fpga_pci_poke(handle, offset, value)
+        check_return_code(ret, "pci poke", -1)
+
+    def pci_poke8(self, handle: pci_bar_handle_t, offset: uint64_t, value: uint8_t) -> None:
+        ret = fpga_pci_poke8(handle, offset, value)
+        check_return_code(ret, "pci poke", -1)
+
+    def pci_poke64(self, handle: pci_bar_handle_t, offset: uint64_t, value: uint64_t) -> None:
+        ret = fpga_pci_poke64(handle, offset, value)
+        check_return_code(ret, "pci poke", -1)
+
+    def pci_write_burst(self, handle: pci_bar_handle_t, offset: uint64_t, data: List[int], dword_len: uint64_t) -> None:
+        cdef uint32_t* datap = <uint32_t*>malloc(dword_len * sizeof(uint32_t))
+        if not datap:
+            raise MemoryError("Failed to allocate memory for burst write")
+        try:
+            for i in range(dword_len):
+                datap[i] = data[i]
+            ret = fpga_pci_write_burst(handle, offset, datap, dword_len)
+            check_return_code(ret, "pci write burst", -1)
+        finally:
+            free(datap)
+
+    def pci_peek(self, handle: pci_bar_handle_t, offset: uint64_t) -> uint32_t:
+        cdef uint32_t value
+        ret = fpga_pci_peek(handle, offset, &value)
+        check_return_code(ret, "pci peek", -1)
+        return value
+
+    def pci_peek8(self, handle: pci_bar_handle_t, offset: uint64_t) -> uint8_t:
+        cdef uint8_t value
+        ret = fpga_pci_peek8(handle, offset, &value)
+        check_return_code(ret, "pci peek", -1)
+        return value
+
+    def pci_peek64(self, handle: pci_bar_handle_t, offset: uint64_t) -> uint64_t:
+        cdef uint64_t value
+        ret = fpga_pci_peek64(handle, offset, &value)
+        check_return_code(ret, "pci peek", -1)
+        return value
+
+    def pci_get_slot_spec(self, slot_id: int) -> fpga_slot_spec:
+        cdef fpga_slot_spec spec
+        ret = fpga_pci_get_slot_spec(slot_id, &spec)
+        check_return_code(ret, "get slot spec", slot_id)
+        return spec
+
+    def pci_get_all_slot_specs(self, size: int) -> List[fpga_slot_spec]:
+        cdef fpga_slot_spec* spec_array = <fpga_slot_spec*>malloc(size * sizeof(fpga_slot_spec))
+        if spec_array == NULL:
+            raise MemoryError("Failed to allocate memory for spec array")
+
+        ret = fpga_pci_get_all_slot_specs(spec_array, size)
+        check_return_code(ret, "get all slot specs", -1)
+        specs = [spec_array[i] for i in range(size)]
+        free(spec_array)
+        return specs
+
+    def pci_get_resource_map(self, slot_id: int, pf_id: int) -> fpga_pci_resource_map:
+        cdef fpga_pci_resource_map map
+        ret = fpga_pci_get_resource_map(slot_id, pf_id, &map)
+        check_return_code(ret, "get resource map", slot_id)
+        return map
+
+    def pci_rescan_slot_app_pfs(self, slot_id: int) -> None:
+        ret = fpga_pci_rescan_slot_app_pfs(slot_id)
+        check_return_code(ret, "rescan slot app pfs", slot_id)
+
+    def pci_get_address(self, handle: pci_bar_handle_t, offset: uint64_t, dword_len: uint64_t):
+        cdef void* ptr
+        ret = fpga_pci_get_address(handle, offset, dword_len, &ptr)
+        check_return_code(ret, "pci get address", -1)
+        return  <uintptr_t>ptr
+
+    def pci_memset(self, handle: pci_bar_handle_t, offset: uint64_t, value: uint32_t, dword_len: uint64_t) :
+        ret = fpga_pci_memset(handle, offset, value, dword_len)
+        return check_return_code(ret, "memset", -1)
diff --git a/sdk/userspace/cython_bindings/fpga_utils.pyx b/sdk/userspace/cython_bindings/fpga_utils.pyx
new file mode 100644
index 000000000..0cb3fa2b5
--- /dev/null
+++ b/sdk/userspace/cython_bindings/fpga_utils.pyx
@@ -0,0 +1,12 @@
+# fpga_utils.pyx
+from fpga_mgmt cimport fpga_mgmt_strerror
+from logging import getLogger
+
+logger = getLogger(__name__)
+
+def check_return_code(ret: int, check: str, slot_id: int) -> None:
+    slot_str = "N/A" if slot_id == -1 else str(slot_id)
+    if ret != 0:
+        error_message = fpga_mgmt_strerror(ret).decode('utf-8')
+        raise RuntimeError(f"Failed to {check} on slot: {slot_str}, error code: {ret}: {error_message}")
+    logger.info(f"Succeeded to {check} on slot: {slot_str}")
diff --git a/sdk/userspace/cython_bindings/setup.py b/sdk/userspace/cython_bindings/setup.py
new file mode 100644
index 000000000..3f6a15644
--- /dev/null
+++ b/sdk/userspace/cython_bindings/setup.py
@@ -0,0 +1,63 @@
+from setuptools import setup, Extension
+from Cython.Build import cythonize
+import os
+
+fpga_mgmt_extension = Extension(
+    name="fpga_mgmt_wrapper",
+    sources=["fpga_mgmt_wrapper.pyx"],
+    libraries=["fpga_mgmt"],
+    library_dirs=[
+        os.path.join(os.environ["SDK_DIR"], "userspace/lib/so"),
+        os.path.join(os.environ["SDK_DIR"], "userspace/lib"),
+    ],
+    include_dirs=[os.path.join(os.environ["SDK_DIR"], "userspace/include")],
+    extra_compile_args=["-Wno-address-of-packed-member"],
+)
+
+fpga_pci_extension = Extension(
+    name="fpga_pci_wrapper",
+    sources=["fpga_pci_wrapper.pyx"],
+    libraries=["fpga_mgmt"],
+    library_dirs=[
+        os.path.join(os.environ["SDK_DIR"], "userspace/lib/so"),
+        os.path.join(os.environ["SDK_DIR"], "userspace/lib"),
+    ],
+    extra_objects=[os.path.join(os.environ["SDK_DIR"], "userspace/lib/libfpga_pci.a")],
+    include_dirs=[os.path.join(os.environ["SDK_DIR"], "userspace/include")],
+    extra_compile_args=["-Wno-address-of-packed-member"],
+)
+
+fpga_clkgen_extension = Extension(
+    name="fpga_clkgen_wrapper",
+    sources=["fpga_clkgen_wrapper.pyx"],
+    libraries=["fpga_mgmt"],
+    library_dirs=[os.path.join(os.environ["SDK_DIR"], "userspace/lib/so")],
+    extra_objects=[
+        os.path.join(os.environ["SDK_DIR"], "userspace/lib/libfpga_clkgen.a")
+    ],
+    include_dirs=[os.path.join(os.environ["SDK_DIR"], "userspace/include")],
+    extra_compile_args=["-Wno-address-of-packed-member"],
+)
+
+fpga_utils_extension = Extension(
+    name="fpga_utils",
+    sources=["fpga_utils.pyx"],
+    libraries=["fpga_mgmt"],
+    library_dirs=[os.path.join(os.environ["SDK_DIR"], "userspace/lib/so")],
+    include_dirs=[os.path.join(os.environ["SDK_DIR"], "userspace/include")],
+    extra_compile_args=["-Wno-address-of-packed-member"],
+)
+
+
+setup(
+    name="fpga_wrappers",
+    ext_modules=cythonize(
+        [
+            fpga_utils_extension,
+            fpga_mgmt_extension,
+            fpga_pci_extension,
+            fpga_clkgen_extension,
+        ],
+        language_level=3,
+    ),
+)
diff --git a/sdk/userspace/cython_bindings/utils.py b/sdk/userspace/cython_bindings/utils.py
new file mode 100644
index 000000000..b7d343ad0
--- /dev/null
+++ b/sdk/userspace/cython_bindings/utils.py
@@ -0,0 +1,16 @@
+import logging, json
+
+
+def setup_logger() -> None:
+    # Change logging level to logging.INFO for verbose Cython methods
+    logging.basicConfig(
+        level=logging.WARNING,
+        format="%(asctime)s - %(levelname)s - %(message)s",
+        datefmt="%Y-%m-%d %H:%M:%S",
+    )
+
+
+def convert_info_to_json(info: dict) -> str:
+    afi_id_decoded = info["afi_id"]["afi_id"].decode("utf-8")
+    info["afi_id"]["afi_id"] = afi_id_decoded
+    return json.dumps(info, indent=2)
diff --git a/sdk_setup.sh b/sdk_setup.sh
index c973fb44f..210c0f705 100644
--- a/sdk_setup.sh
+++ b/sdk_setup.sh
@@ -55,6 +55,15 @@ if ! bash $SDK_DIR/sdk_install.sh; then
     return 1
 fi
 
+cd sdk/userspace/cython_bindings
+sudo apt install -y python3-venv python3-pip
+python3 -m venv venv
+source venv/bin/activate
+pip install setuptools Cython
+python3 setup.py build_ext --inplace
+deactivate
+echo "Cython bindings setup complete!"
+
 cd $current_dir
 info_msg "$script_name PASSED"
 
diff --git a/vitis_setup.sh b/vitis_setup.sh
index f561a7761..e93f9a602 100644
--- a/vitis_setup.sh
+++ b/vitis_setup.sh
@@ -174,11 +174,13 @@ function sha256_check {
 function get_sha_file {
     missing_sha="${missing_xsa}.sha256"
     sha_path="${destination_dir}/${missing_sha}"
-    if [ ! -e "${sha_path}" ]; then
-        if ! sudo wget "${vitis_xsa_s3_url}/${missing_sha}" -O "${sha_path}" -q; then
-            err_msg "Download of Vitis XSA SHA256 file ${missing_sha} failed!"
-            return 1
-        fi
+    if [ -e "${sha_path}" ]; then
+        rm $sha_path
+    fi
+
+    if ! sudo wget "${vitis_xsa_s3_url}/${missing_sha}" -O "${sha_path}" -q; then
+        err_msg "Download of Vitis XSA SHA256 file ${missing_sha} failed!"
+        return 1
     fi
 
     xsa_path="${destination_dir}/${missing_xsa}"
@@ -201,7 +203,8 @@ function get_xsa_file {
 
     # Remove the old XSA
     missing_xsa_file_extension="${missing_xsa##*.}"
-    sudo rm -f "${destination_dir}/*.${missing_xsa_file_extension}"
+    sudo rm -f ${destination_dir}/*${missing_xsa_file_extension}
+    sudo rm -f ${destination_dir}/*${missing_xsa_file_extension}.sha256
 
     # Grab the new XSA
     if ! sudo wget "${vitis_xsa_s3_url}/${missing_xsa}" -O "${destination_dir}/${missing_xsa}"  -q; then
@@ -250,8 +253,10 @@ xsa_map["2024.2"]="202420_1"
 function setup_xsa {
 
     info_msg "Installing supporting libraries"
-    sudo $XILINX_VITIS/scripts/installLibs.sh >>/dev/null
-    rm installLibs.sh_*
+    if [[ -z $(lsmod | grep xocl) ]]; then
+        sudo $XILINX_VITIS/scripts/installLibs.sh >>/dev/null
+        rm installLibs.sh_*
+    fi
 
     # Get XSA for right tool version
     xsa_for_tool_ver="${xsa_map[${VITIS_TOOL_VER}]}"
