To create a custom hardware overlay (.bit and .hwh) that can be loaded onto a running PYNQ system without rebuilding the Linux OS.
We will design a system containing:
We will create a logic block that counts ticks. This demonstrates internal state retention, which simple combinational logic (like an adder) does not have.
ip_counter_temp. Click Next.pynq.axi_dyn_counter.My new AXI IP (optional).axi_dyn_counter_v1_0.v: The top-level wrapper (instantiates the AXI logic).axi_dyn_counter_v1_0_S00_AXI.v: The actual AXI Lite implementation. Edit this one.
_v1_0.v): This file is just the shell. Its job is to group multiple interfaces together. For example, if your IP had an AXI-Lite port plus an AXI-Stream video port plus an Interrupt line, the wrapper connects them all into one black box._S00_AXI.v): This file contains the AXI Protocol Engine. Vivado has pre-written the complex code that handles the valid/ready handshakes and address decoding. It gives you simple variables (slv_reg0) to work with. If you wrote code in the wrapper, you would have to write the AXI state machine yourself!Open axi_dyn_counter_v1_0_S00_AXI.v.
// Add user logic here
// User logic ends
Paste the following code between those lines:
// -- Custom Signals --
reg [31:0] internal_count;
wire enable_signal;
wire reset_signal;
// -- Mappings --
// slv_reg0[0] = Enable
// slv_reg0[1] = Reset
assign enable_signal = slv_reg0[0];
assign reset_signal = slv_reg0[1];
// -- Counter Logic --
always @( posedge S_AXI_ACLK ) begin
if ( S_AXI_ARESETN == 1'b0 || reset_signal == 1'b1 ) begin
internal_count <= 0;
end else if ( enable_signal == 1'b1 ) begin
internal_count <= internal_count + 1;
end
end
2. Map Output (The Read):
Scroll up slightly (around line 303). Find the long line that handles reading data (assign S_AXI_RDATA = ...).
Change: ... ? slv_reg1 : ...
To: ... ? internal_count : ...
Old Line (Simplified):
assign S_AXI_RDATA = (... == 2'h0) ? slv_reg0 : (... == 2'h1) ? slv_reg1 : ...
New Line:
assign S_AXI_RDATA = (... == 2'h0) ? slv_reg0 : (... == 2'h1) ? internal_count : ...
Effect: When the processor reads Register 1 (Offset 0x4), it now sees our live internal_count instead of the static slv_reg1 value.
Now we build the system that PYNQ will load.
Why a new project?
- Separation of Concerns:
ip_counter_tempis like a “Library Project” (specifically for creating the component).pynq_overlay_demois the “Application Project” (where we wire components together).- Reusability: By packaging the IP separately, you can now use
axi_dyn_counterin any future Vivado project, just like you use the standard GPIO block.
pynq_overlay_demo.ip_repo folder (usually inside ip_counter_temp/ip_repo).This is the secret sauce. PYNQ needs two files:
Now we switch to the Jupyter Notebook interface.
from pynq import Overlay
import time
# 1. Load the Overlay
ol = Overlay("my_counter.bit")
# 2. Check IP Dictionary
print("Available IPs:", ol.ip_dict.keys())
# 3. Create Drivers
# Standard PYNQ driver for LEDs (High-Level)
leds = ol.axi_gpio_0.channel1
# Custom IP access via MMIO (Low-Level)
counter = ol.axi_dyn_counter_0
Cell 2: Test LEDs (Standard GPIO)
# Configure Direction (TRI Register)
# The High-Level driver defaults to Input. We must force Output.
# Offset 0x4 = Channel 1 Tri-State Control (0 = Output, 1 = Input)
ol.axi_gpio_0.mmio.write(0x4, 0x0)
print("Direction set to Output via MMIO.")
print("Blinking LEDs...")
for i in range(4):
leds.write(0xF, 0xF) # All ON
time.sleep(0.2)
leds.write(0x0, 0xF) # All OFF
time.sleep(0.2)
print("Done.")
Cell 3: Test Custom Counter (MMIO)
print("Testing Custom Counter...")
# Reset (Write 2 to Reg0)
counter.write(0x0, 2)
# Enable (Write 1 to Reg0)
counter.write(0x0, 1)
print("Counting for 1 second...")
time.sleep(1.0)
# Stop (Write 0 to Reg0)
counter.write(0x0, 0)
# Read Result (Read Reg1 @ Offset 0x4)
count_val = counter.read(0x4)
# Math check: Clock is 100MHz. 1 second ~ 100M ticks.
print(f"Counter Value: {count_val}")
print(f"Frequency: {count_val / 1_000_000:.2f} MHz")
Next Steps: You now have the skills to build the hardware for any custom accelerator.
The hardware is done. Now, let’s look at where you go from here.