Real-Time 1 MSPS Hardware-Triggered PYNQ Oscilloscope
A high-performance, dark-mode real-time Oscilloscope software stack running natively on PYNQ Linux platforms.
Features sub-microsecond hardware-level edge triggering (axis_trigger_unit), 1 MSPS XADC streaming via AXI DMA direct to DDR memory, and non-blocking analog signal generation with the Digilent Analog Discovery 3 via pydwf.
🏛 System Architecture
This repository adopts the canonical PYNQ Custom Overlay pattern (OscilloscopeOverlay). It automatically pulls its compiled hardware bitstream and metadata from GitHub Releases (or loads local custom .bit builds) and encapsulates the DMA receiver, AXI-Lite trigger registers, and wavegen into a unified Python object.
[ Analog Discovery 3 (W1) ] ──(Analog Jumper Wire)──> [ PYNQ-Z2 Header (A0) ]
│ │
(pydwf SDK) (XADC 1 MSPS AXI-Stream)
│ │
▼ ▼
[ AD3SignalGenerator ] [ axis_trigger_unit IP ]
│ (Edge, Threshold, Auto Timeout)
│ │
│ ▼
│ [ AXI DMA S2MM Engine ]
│ │
└───────────────────────┬─────────────────────────────┘
│
▼
[ OscilloscopeOverlay ]
(Subclasses pynq.Overlay with sub-drivers)
├── .trigger (HardwareTrigger AXI-Lite)
├── .xadc (StreamingXADC DMA Driver)
├── .wavegen (AD3SignalGenerator)
└── .dashboard() (Interactive Plotly Canvas)
🖥 Interactive Dashboard UI Guide
1. Control & Action Bar (Row 1)
▶ Start: Initializes the background acquisition worker, turns on AD3 waveform generation, arms the FPGA trigger, and begins DMA streaming.■ Stop: Cleanly halts the acquisition loop, disarms the trigger, stops the AD3 wavegen, and releases device handles.⚡ Force / Arm:- In Single Mode, re-arms the FPGA trigger unit to capture the next transient event.
- In any mode, pulses bit 4 of
CONTROL_REG(0x00) to force an immediate hardware frame capture.
Auto-Range(Toggle): Dynamically scales the visible horizontal timebase (showing 5–10 signal periods) and adapts the vertical Y-axis limits ($1.65,\text{V} \pm \text{Amplitude}$ with margin).Live Vpp: Real-time peak-to-peak voltage calculation updated live ($V_{pp} = V_{\max} - V_{\min}$).
2. Hardware Trigger Controls (Row 2)
Trig Mode:Auto: Continuous live stream. Locks onto trigger edges when present; if no edge occurs within 50 ms (e.g., disconnected input or threshold out of range), the hardware auto-timeout forces a frame capture so the display never freezes.Normal: Strictly edge-triggered. The FPGA only captures and transfers data to DDR memory when a valid trigger event occurs.Single: Captures one single frame on the first trigger event and freezes the display. Re-arm by clicking⚡ Force / Arm.
Trig Edge(Rising/Falling): Configures whether the FPGA comparator triggers on the upward slope ($\nearrow$) or downward slope ($\searrow$).Trig Level(Slider & Numeric Box): Sets the FPGA voltage threshold register (0x08) between $0.0,\text{V}$ and $3.3,\text{V}$ with client-side zero-latency linking (widgets.jslink).
3. AD3 Signal Generator Controls (Rows 3 & 4)
Waveform(Sine,Triangle,Square): Selects the DAC output waveform on AD3 Wavegen Channel 1 (W1).Amp Slider / Exact Amp: Adjusts the signal amplitude in Volts ($0.1,\text{V}$ to $1.5,\text{V}$).Freq Slider / Exact Freq: Sets the generation frequency in Hertz ($100,\text{Hz}$ to $1,\text{MHz}$).
4. Interactive Plotly Canvas
- Cyan Trace (
A0 (Analog In)): 1 MSPS analog signal stream read directly from DDR memory. Sample $[0]$ ($t=0,\mu\text{s}$) is hardware-aligned to the trigger edge. - Orange Dashed Trace (
Trigger Level): Live visual threshold line reflecting the FPGA trigger register level.
🔌 Hardware Setup & Wiring
- AD3 USB Connection:
- Plug the Analog Discovery 3 USB cable into the large rectangular USB HOST port on the PYNQ-Z2 board (adjacent to Ethernet).
- USB Cable Quality:
- Ensure you use a Data + Power USB-C cable (charging-only cables will not be detected by Linux).
- Power Supply:
- Power the AD3 with an external 5V auxiliary power supply to prevent brownouts under load.
- Analog Signals:
- Connect a jumper wire from Wavegen 1 (W1) on the AD3 to Analog Input A0 on the PYNQ-Z2 Arduino header.
- Connect an AD3 GND pin to a PYNQ-Z2 GND pin.
🚀 Quick Start & Installation
1. Install Package from PyPI
Connect to your PYNQ board via SSH or Jupyter Terminal and run:
pip install --upgrade pynq-oscilloscope
2. Copy Example Notebooks to Jupyter Workspace
Copy this project's notebooks into /home/xilinx/jupyter_notebooks/pynq_oscilloscope/:
pynq-oscilloscope-get-notebooks
3. Install Digilent AD3 Drivers
Run the automated environment setup inside Python or a Jupyter cell:
from pynq_oscilloscope import install_ad3_drivers
# Downloads Adept Runtime + WaveForms SDK and configures USB permissions
install_ad3_drivers()
💻 Python API Usage
1. Launch Interactive Dashboard in 2 Lines (Default Cloud Fetch)
from pynq_oscilloscope import OscilloscopeOverlay
# Automatically identifies board (PYNQ-Z2), downloads v1.1.0 release, and loads FPGA
ol = OscilloscopeOverlay()
# Launch dark-mode interactive Plotly + IPywidgets dashboard
app = ol.dashboard()
2. Load Local Custom Bitstream (Offline / Development)
from pynq_oscilloscope import OscilloscopeOverlay
# Load a local bitstream while preserving all driver hooks and UI tools
ol = OscilloscopeOverlay("./pynq_z2.bit")
app = ol.dashboard()
3. Programmatic Hardware Trigger & DMA Capture
from pynq_oscilloscope import OscilloscopeOverlay
ol = OscilloscopeOverlay()
# Configure FPGA Trigger: Rising Edge @ 1.65V with 50 ms Auto-timeout
ol.trigger.configure(mode="Auto", edge="Rising", threshold_volts=1.65, timeout_ms=50.0)
# Capture 16,384 samples (Sample [0] is guaranteed hardware-aligned to trigger point!)
voltages = ol.capture()
print(f"Captured {len(voltages)} samples. Min: {voltages.min():.2f}V, Max: {voltages.max():.2f}V")
# Clean release of memory buffers
ol.close()
📓 Notebook Suite
| Notebook | Description | Key Modules Used |
|---|---|---|
01_ad3_getting_started.ipynb |
Verifies Digilent drivers and generates analog signals (Sine, Square, Triangle) in a non-blocking background worker. | AD3SignalGenerator, check_usb_permissions |
02_xadc_getting_started.ipynb |
Demonstrates OscilloscopeOverlay, hardware register trigger configuration (ol.trigger), and DMA capture. |
OscilloscopeOverlay, HardwareTrigger |
03_oscilloscope_dashboard.ipynb |
Main Application: Deploys the complete interactive Plotly Oscilloscope with live trigger line, auto-ranging, and AD3 integration. | OscilloscopeOverlay |
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
Metadata
Release files for pynq-oscilloscope 1.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pynq_oscilloscope-1.1.0.tar.gz | 22.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pynq_oscilloscope-1.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 46.9 kB
Release files / pynq_oscilloscope-1.1.0.tar.gz
| Download URL | pynq_oscilloscope-1.1.0.tar.gz |
|---|---|
| Size | 22.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d03c5ce5cbc76170cf376c091615605c0a9b74e27c0011fe29e14c9c81b67b1c
|
|
BLAKE2b-256 checksum How to use checksums |
8f85edd32c8e6e4ac6d4a51d7fbdde5ad362eeb481bbe8ba95abcc458547bf7b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 14, 2026.
Transparency logRelease files / pynq_oscilloscope-1.1.0-py3-none-any.whl
| Download URL | pynq_oscilloscope-1.1.0-py3-none-any.whl |
|---|---|
| Size | 24.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
586613b0e67be65b0e7e652abc16a58c30a01778942f5103f50cb7b309750306
|
|
BLAKE2b-256 checksum How to use checksums |
b1474e16c096517fa1655f96f00a220a3b3b481b8a5e3469e9f18b705e0c3b6f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 14, 2026.
Transparency log