FPGA development has traditionally meant writing Verilog or VHDL—powerful but verbose languages that can feel disconnected from modern software workflows. What if you could describe hardware using Python, the same language you use for software development, testing, and data analysis?
This project demonstrates how to create a custom FPGA IP core using MyHDL (a Python-based hardware description language) and integrate it into a Xilinx Vivado design for the KR260 board. By leveraging Python's expressiveness and MyHDL's hardware conversion capabilities, we can design, simulate, and deploy FPGA logic using familiar tools and syntax.
The result: an interrupt generator IP that supports both periodic (timer-based) and software-triggered interrupts, fully integrated with the Zynq UltraScale+ PS via AXI4-Lite, and controllable through PYNQ's Python interface.
Why MyHDL?MyHDL bridges the gap between software and hardware development:
- Python Syntax: Write hardware descriptions in familiar Python code
- Simulation Support: Test hardware logic in Python before synthesis
- Code Generation: Automatic Verilog conversion for Vivado integration
- Modular Design: Reusable interface definitions and components
- Type Safety: Hardware-aware type checking prevents common errors
This project builds on the foundation established in my Custom Yocto Linux & Network Boot for Xilinx KR260 project, which provides the PYNQ-enabled Linux environment needed to run the software side of this design.
Project OverviewThe interrupt generator IP provides:
- Two Independent Interrupt Outputs: Separate interrupt1 and interrupt2 channels
- Periodic Interrupt Mode: Configurable timer-based interrupts with 32-bit period counters
- Software-Triggered Mode: On-demand interrupt generation via register writes
- Register-Based Control: AXI4-Lite interface for configuration and status monitoring
- Interrupt Management: Status and enable registers for flexible interrupt control
Hardware:
- Xilinx KR260 Robotics Starter Kit
- Network connection (for PYNQ access)
Software:
- Ubuntu 24.04 LTS (build host)
- Python 3.12
- Vivado 2025.2 (or compatible)
- Custom Yocto Linux image with PYNQ (from previous project)
- Jupyter Notebook server (included in PYNQ image)
Build Dependencies:
sudo apt-get update
sudo apt-get install -y python3.12 python3.12-venv makeProject StructureThe build process converts Python-based MyHDL code to Verilog for Vivado integration.
Step 1: Build the IP
cd myhdl
make allThis command:
- Creates a Python 3.12 virtual environment
- Installs the MyHDL package
- Converts MyHDL code to Verilog
- Outputs:
build/interrupt_generator_ip.v
Manual Build Process:
# Create virtual environment
python3.12 -m venv venv
source venv/bin/activate
# Install MyHDL
pip install --upgrade pip
pip install myhdl
# Convert to Verilog
PYTHONPATH=. python PL/MyHDL/src/interrupt_generator_ip/interrupt_generator_ip.pyThe generated Verilog file (interrupt_generator_ip.v) contains the complete IP core with AXI4-Lite interface, ready for Vivado integration.
Core Components:
- Interrupt Generator Core (
interrupt_gen.py)Implements periodic and software-triggered interrupt logicManages period counters and interrupt statusProvides two independent interrupt outputs - IP Wrapper (
interrupt_generator_ip.py)Converts AXI4-Lite interface to internal AxiLocal formatInstantiates the interrupt generator coreGenerates Verilog output for Vivado - AXI Interfaces (
axi_lite.py,axi_local.py)Standard AXI4-Lite slave interfaceSimplified local AXI bus for internal routing
Register Map:
Step 1: Create New Project
- Open Vivado and select Create Project
- Choose RTL Project and click Next
- Set project name:
interrupt_demo - Select project location
- Choose Do not specify sources at this time
- Click Boards and select "Kria KR260 Robotics Starter Kit SOM"
Step 2: Add MyHDL-Generated Verilog
- In Vivado, right-click Design Sources → Add Sources
- Select Add or create design sources
- Click Add Files and navigate to
build/interrupt_generator_ip.v - Click Finish
Step 3: Create Block Design
- Right-click in Sources window → Create Block Design
- Name it
interrupt_demo - Click OK
Step 4: Add IP Cores
Add the following IP cores to the block design:
- Zynq UltraScale+ PS (
zynq_ultra_ps_e)Double-click to configureEnable PL-PS Clock (100MHz)Enable M_AXI_HPM0_LPD interfaceEnable IRQ_F2P interrupt input - Interrupt Generator IP (Custom IP)Right-click in block design → Add ModuleSelect
interrupt_generator_ip_0from the listThis is the MyHDL-generated IP
- AXI Smart Memory Controller (
axi_smc)Connects PS to custom IP via AXI4-Lite - AXI Interrupt Controller (
axi_intc)Consolidates PL interruptsBase address:0xB0010000
- Interrupt Concatenator (
xlconcat)Combines interrupt generator outputsConnects to AXI Interrupt Controller - Processor System Reset (
proc_sys_reset)Reset controller for PL logic
Step 5: Connect IP Cores
Connect the following:
- PS → AXI SMC → Interrupt Generator: AXI4-Lite interface
- Interrupt Generator → Interrupt Concatenator: Both interrupt outputs
- Interrupt Concatenator → AXI Interrupt Controller: Combined interrupt bus
- AXI Interrupt Controller → PS: IRQ_F2P interrupt input
- PS → All IPs: Clock and reset signals
Step 6: Create HDL Wrapper
- Right-click block design → Create HDL Wrapper
- Select Let Vivado manage wrapper and auto-update
- Click OK
Step 7: Complete Block Design
The final block design should look like this:
Step 8: Generate Bitstream
- Run Synthesis (Flow Navigator → Synthesis → Run Synthesis)
- Run Implementation (Flow Navigator → Implementation → Run Implementation)
- Generate Bitstream (Flow Navigator → Program and Debug → Generate Bitstream)
Step 9: Export Hardware
- File → Export → Export Hardware
- Include bitstream
- Export to
interrupt_demo.xsa
With the hardware platform exported, we can now interact with the custom IP using PYNQ's Python interface.
Step 1: Copy Files to KR260
Copy the following to the KR260 board:
interrupt_demo.bit→/nfsroot/lib/firmware/interrupt_demo.hwh→/nfsroot/lib/firmware/APU/pynq_interrupt.ipynb→/nfsroot/home/xilinx/Notebook/
Step 2: Open Jupyter Server
On the Ubuntu build/development machine:
firefox http://172.20.1.2:9090/Navigate to pynq_interrupt.ipynb and open it.
Step 3: Load Overlay
from pynq import Overlay, Interrupt
import asyncio
# Load the FPGA overlay
overlay = Overlay('interrupt_demo.bit')
overlay.download()
# Get the interrupt generator IP
intr = overlay.interrupt_generator_ip_0
# Configure PL clock (100MHz)
overlay.clock_dict['fclk_clk0'] = 100000000Step 4: Configure Interrupts
# Register offsets
period1 = 0x00
period2 = 0x04
isr = 0x08
ier = 0x0C
trigger = 0x10
# Configure interrupt1 for software triggering
intr.write(period1, 0) # Disable periodic mode
intr.write(ier, 1) # Enable interrupt1
# Configure interrupt2 for periodic interrupts (1 second at 100MHz)
intr.write(period2, 100000000)
intr.write(ier, 2) # Enable interrupt2Step 5: Handle Interrupts
# Create interrupt instances
intr_inst1 = Interrupt('axi_intc', intr_id=0) # interrupt1
intr_inst2 = Interrupt('axi_intc', intr_id=1) # interrupt2
# Async interrupt handler
async def interrupt_handler(interrupt_object, interrupt_bit):
print("Handler task started. Waiting for interrupt...")
# Wait for interrupt
await interrupt_object.wait()
print("Interrupt received!")
# Clear the interrupt (write 1 to the bit to clear it)
intr.write(isr, interrupt_bit)
# Trigger software interrupt
intr.write(trigger, 1) # Trigger interrupt1
# Wait for interrupt
await interrupt_handler(intr_inst1, 1)
# Periodic interrupt will fire automatically
await interrupt_handler(intr_inst2, 2)Interrupt Operation ModesPeriodic Interrupts:
- Set period register to desired clock cycle count
- Enable interrupt in IER register
- Interrupt fires automatically when counter expires
- ISR bit is set when interrupt occurs
- Software clears ISR bit to acknowledge
Software-Triggered Interrupts:
- Set period register to 0 (disables periodic mode)
- Enable interrupt in IER register
- Write 1 to TRIGGER register bit
- Interrupt fires immediately
- ISR bit is set
- Software clears ISR bit to acknowledge
MyHDL Advantages:
- Rapid Prototyping: Write hardware in Python, test in Python, convert to Verilog
- Code Reuse: Modular design with reusable interface definitions
- Simulation: Test hardware logic before synthesis
- Maintainability: Python's readability makes hardware code easier to understand
Integration Benefits:
- Standard Interfaces: AXI4-Lite provides standard connectivity to PS
- PYNQ Compatibility: Seamless Python control of custom IP
- Flexible Configuration: Register-based control enables runtime reconfiguration
- Interrupt Support: Proper PL-to-PS interrupt handling
This interrupt generator IP can be extended for:
- Real-time Task Scheduling: Periodic interrupts for time-critical operations
- Event Notification: Software-triggered interrupts for asynchronous events
- Hardware Monitoring: Status polling with interrupt-driven updates
- Custom Peripherals: Foundation for more complex custom IP cores
The MyHDL framework makes it easy to extend this design:
- Add More Interrupts: Extend the interrupt generator to support more channels
- Add Features: Implement additional registers for configuration
- Create New IP: Use the same AXI interface pattern for other custom IP
- Simulate First: Test new features in Python before Verilog conversion
- MyHDL Documentation: http://www.myhdl.org/
- PYNQ Documentation: https://pynq.readthedocs.io/
- Xilinx Vivado Documentation: https://www.xilinx.com/support/documentation/sw_manuals/xilinx2025_2/ug910-vivado-getting-started.pdf
- KR260 Board Documentation: https://www.xilinx.com/products/boards-and-kits/kria.html
- Custom Yocto Linux Project: https://www.hackster.io/wstanislaus/custom-yocto-linux-network-boot-for-xilinx-kr260-e22ac3
This project demonstrates that FPGA development doesn't have to mean abandoning modern software tools. By using MyHDL, we can:
- Write hardware in Python
- Leverage Python's ecosystem for testing and simulation
- Generate standard Verilog for Vivado integration
- Control hardware through PYNQ's Python interface
The result is a development workflow that feels natural to software engineers while maintaining the power and flexibility of FPGA design. Whether you're prototyping new IP cores, experimenting with hardware-software co-design, or building production systems, MyHDL provides a bridge between the software and hardware worlds.









Comments