Skip to content

Commit 903f184

Browse files
committed
E2E valIidation of the learning journey.
Scrapped the original Phase 4 in favor of jumping off points and tie-in to the loopback project.
1 parent deb5751 commit 903f184

7 files changed

Lines changed: 208 additions & 239 deletions

File tree

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@ This series creates the foundational knowledge required to leave the "Python saf
2121
* **Phase 3: The Vivado Flow (Hardware Definition)**
2222
* Introduction to Xilinx Vivado.
2323
* Creating custom hardware definitions (Bitstreams/XSA) tailored to specific needs.
24-
* **Phase 4: The Embedded Linux Flow (OS Construction)**
25-
* Moving beyond the pre-supplied OS.
26-
* Constructing a custom Linux operating system specifically for your hardware design.
24+
* **Phase 4: Beyond the Basics (Graduation)**
25+
* Consolidation of skills (Hybrid Development).
26+
* Pathways to specialization (HLS, Video, OS Construction).
27+
* Handoff to the **Loopback Project** for advanced users.
2728

2829
---
2930

index.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ Before building complex systems, we must understand the tools. These documents s
2323
* **[Phase 3: The Vivado Flow (Hardware Definition)](learning_phase3.md)**
2424
* **Goal:** Learn the Language of Hardware.
2525
* **Content:** Introduction to Xilinx Vivado, block designs, synthesis, and bitstream generation.
26-
* **[Phase 4: The Embedded Linux Flow (OS Construction)](learning_phase4.md)**
27-
* **Goal:** Build the Brain.
28-
* **Content:** Introduction to PetaLinux, configuring the Linux kernel, and generating boot images tailored to your custom silicon.
26+
* **[Phase 4: Beyond the Basics (Graduation)](learning_phase4.md)**
27+
* **Goal:** Graduation.
28+
* **Content:** Hybrid development, HLS, Video, and the handoff to the advanced Loopback Project.
2929

3030
---
3131

learning_overview.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,13 @@ Ultimate Goal: To possess the full-stack engineering skills required to bring up
5757
* Add **AXI GPIO** or custom IP blocks.
5858
* Generate Bitstream and Hardware Handoff files (.hwh).
5959

60-
### **Phase 4: The Embedded Linux Flow (OS Construction)**
60+
### **Phase 4: Beyond the Basics (Graduation)**
6161

62-
* **Goal:** Build the OS from source, bypassing the PYNQ abstraction.
62+
* **Goal:** Consolidation and specialization.
6363
* **Tasks:**
64-
* Setup a Linux build host (Ubuntu 20.04/22.04).
65-
* **Method A (Easier):** PYNQ SDBuild flow to generate custom SD images.
66-
* **Method B (Harder/Industry Standard):** PetaLinux flow. Manually configuring Kernel, U-Boot, and RootFS.
64+
* Explore High-Level Synthesis (HLS).
65+
* Understand Video Pipelines.
66+
* **The Fork:** Choose to stay in Python or dive into the OS (Loopback Project).
6767

6868
## **5\. The "Loopback" Proof Project**
6969

learning_phase1.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ Ensure you have the physical assets required to power and connect the board.
2424
### **Part A: Prepare the Boot Medium**
2525

2626
1. **Download Image:**
27-
* Go to [PYNQ.io Boards Page](https://www.google.com/search?q=http://www.pynq.io/board.html).
27+
* Go to [PYNQ.io Boards Page](https://www.pynq.io/boards.html).
2828
* Locate the **PYNQ-Z2** section.
2929
* Download the latest SD Card Image (e.g., v3.0.1 or v2.7). *Note: This is a large zip file.*
3030
2. **Flash the SD Card:**

learning_phase2.md

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -51,22 +51,25 @@ PYNQ provides Python classes that wrap the complex AXI protocol into simple obje
5151
1. Control LEDs:
5252
The base overlay object automatically creates drivers for known peripherals.
5353
```python
54-
from pynq.lib import LED, Button
54+
# Access the LED GPIO block directly
55+
# Note: PYNQ maps IP blocks using their Vivado names (e.g., 'leds_gpio')
56+
leds = base.leds_gpio
5557

56-
# Address the 4 User LEDs (LD0 - LD3)
57-
led0 = base.leds[0]
58-
led1 = base.leds[1]
58+
# Address the individual LEDs (indices 0-3)
59+
led0 = leds[0]
60+
led1 = leds[1]
5961

60-
# Turn them on/off
61-
led0.on()
62-
led1.off()
62+
# Turn them on/off (Write 1 for High, 0 for Low)
63+
led0.write(1)
64+
led1.write(0)
6365
```
6466

6567
* *Action:* Look at your board. LD0 should be ON.
6668
2. **Read Buttons:**
6769
```python
6870
# Read Button 0 (BTN0)
69-
btn0 = base.buttons[0]
71+
# Note: PYNQ maps this to 'btns_gpio'
72+
btn0 = base.btns_gpio[0]
7073

7174
print(f"Button 0 State: {btn0.read()}")
7275
```
@@ -132,12 +135,12 @@ Run this loop in a new cell:
132135

133136
while True:
134137
# High-Level Read
135-
if base.buttons[3].read() == 1:
138+
if base.btns_gpio[3].read() == 1:
136139
print("Exit command received.")
137140
break
138141

139142
# Logic: Copy Button 0 state to all 4 LEDs
140-
if base.buttons[0].read() == 1:
143+
if base.btns_gpio[0].read() == 1:
141144
# Low-Level Write: Turn all ON (0xF)
142145
led_mmio.write(0x0, 0xF)
143146
else:

learning_phase3.md

Lines changed: 142 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -23,71 +23,116 @@ We will create a logic block that counts ticks. This demonstrates internal state
2323

2424
1. **Create IP Project:**
2525
* Open Vivado. Click **Create Project**.
26-
* Name: ip\_counter\_temp.
27-
* Part: **PYNQ-Z2**.
28-
* Go to **Tools** \> **Create and Package New IP**.
29-
* Select **Create a new AXI4 peripheral**.
30-
* Name: axi\_dyn\_counter.
31-
* Click **Finish** \> **Edit IP**.
32-
2. **Modify Verilog (axi\_dyn\_counter\_v1\_0\_S00\_AXI.v):**
33-
* Open the source file.
34-
* **Add Internal Signals:** Add these lines before the main logic:
26+
* Name: `ip_counter_temp`. Click **Next**.
27+
* **Project Type:** Select **RTL Project** (Do not specify sources). Click **Next**.
28+
* **Project Part:** Switch to the **Boards** tab.
29+
* Search for: `pynq`.
30+
* Select: **pynq-z2**.
31+
* Click **Next**, then **Finish**.
32+
* **Start IP Wizard:** Go to **Tools** > **Create and Package New IP**. Click **Next**.
33+
* **Task:** Select **Create a new AXI4 peripheral**. Click **Next**.
34+
* **Details:**
35+
* Name: `axi_dyn_counter`.
36+
* Description: `My new AXI IP` (optional).
37+
* Click **Next**.
38+
* **Interfaces:** Keep defaults (Lite, Slave, 32-bit, 4 Registers). Click **Next**.
39+
* **Summary:**
40+
* Select **Edit IP** (Important!).
41+
* Click **Finish**.
42+
* *Observation:* A new Vivado window will open specifically for editing this IP.
43+
2. **Modify Verilog Source:**
44+
In the *Sources* panel, you will see two files:
45+
* `axi_dyn_counter_v1_0.v`: The top-level wrapper (instantiates the AXI logic).
46+
* `axi_dyn_counter_v1_0_S00_AXI.v`: The actual AXI Lite implementation. **Edit this one.**
47+
48+
* **Why not the wrapper?**
49+
* **The Wrapper (`_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.
50+
* **The Logic (`_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!
51+
52+
* **Open `axi_dyn_counter_v1_0_S00_AXI.v`.**
53+
54+
* **1. Add Logic (The Counter):**
55+
Scroll to the very bottom of the file (around line 400). You will see a placeholder:
3556
```verilog
36-
reg [31:0] internal_count;
37-
wire enable_signal;
38-
wire reset_signal;
39-
```
57+
// Add user logic here
4058
41-
* **Map Registers:**
42-
```verilog
43-
slv_reg0: Control Register (Bit 0 \= Enable, Bit 1 \= Reset).
44-
slv_reg1: Output Register (The Count).
59+
// User logic ends
4560
```
46-
* **Implement Logic:** Add this block at the end of the file (before endmodule):
47-
```verilog
48-
// Mapping Control Bits
61+
Paste the following code *between* those lines:
62+
```verilog
63+
// -- Custom Signals --
64+
reg [31:0] internal_count;
65+
wire enable_signal;
66+
wire reset_signal;
67+
68+
// -- Mappings --
69+
// slv_reg0[0] = Enable
70+
// slv_reg0[1] = Reset
4971
assign enable_signal = slv_reg0[0];
5072
assign reset_signal = slv_reg0[1];
5173
52-
// Counter Logic
74+
// -- Counter Logic --
5375
always @( posedge S_AXI_ACLK ) begin
54-
if ( S_AXI_ARESETN == 1'b0 || reset_signal == 1'b1 ) begin
55-
internal_count <= 0;
56-
end else if ( enable_signal == 1'b1 ) begin
57-
internal_count <= internal_count + 1;
58-
end
76+
if ( S_AXI_ARESETN == 1'b0 || reset_signal == 1'b1 ) begin
77+
internal_count <= 0;
78+
end else if ( enable_signal == 1'b1 ) begin
79+
internal_count <= internal_count + 1;
80+
end
5981
end
60-
```
82+
```
83+
84+
* **2. Map Output (The Read):**
85+
Scroll up slightly (around line 303). Find the long line that handles reading data (`assign S_AXI_RDATA = ...`).
86+
87+
**Change:** `... ? slv_reg1 : ...`
88+
**To:** `... ? internal_count : ...`
89+
90+
*Old Line (Simplified):*
91+
`assign S_AXI_RDATA = (... == 2'h0) ? slv_reg0 : (... == 2'h1) ? slv_reg1 : ...`
92+
93+
*New Line:*
94+
`assign S_AXI_RDATA = (... == 2'h0) ? slv_reg0 : (... == 2'h1) ? internal_count : ...`
95+
96+
*Effect:* When the processor reads Register 1 (Offset 0x4), it now sees our live `internal_count` instead of the static `slv_reg1` value.
6197
62-
* **Hijack Read Logic:** Find the assign S\_AXI\_RDATA (or case statement) logic we touched in the Loopback project. Modify address h1 to read internal\_count instead of slv\_reg1.
63-
```verilog
64-
// Example for Ternary Syntax (Newer Vivado):
65-
// ... (axi_araddr[...] == 2'h1) ? internal_count : ...
66-
```
6798
6899
3. **Package:**
69-
* **Review and Package** \> **Re-Package IP**.
100+
* Go to the **Package IP...** tab.
101+
* Click **Review and Package** \> **Re-Package IP**.
70102
* Close the temporary IP project.
71103
72104
### **Part B: The Overlay Project**
73105
74106
Now we build the system that PYNQ will load.
75107
108+
> **Why a new project?**
109+
> * **Separation of Concerns:** `ip_counter_temp` is like a "Library Project" (specifically for creating the component). `pynq_overlay_demo` is the "Application Project" (where we wire components together).
110+
> * **Reusability:** By packaging the IP separately, you can now use `axi_dyn_counter` in *any* future Vivado project, just like you use the standard GPIO block.
111+
76112
1. **Create Project:**
77-
* Create a new RTL Project: pynq\_overlay\_demo.
113+
* Create a new RTL Project: `pynq_overlay_demo`.
78114
* Board: **PYNQ-Z2**.
79-
2. **Setup Block Design:**
115+
2. **Add IP Repository:**
116+
(Crucial for finding your custom IP)
117+
* In the **Flow Navigator** (left), click **Settings**.
118+
* Go to **IP** > **Repository**.
119+
* Click **+** (Add Repository).
120+
* Navigate to your `ip_repo` folder (usually inside `ip_counter_temp/ip_repo`).
121+
* Select it and click **Select**. Click **OK**.
122+
3. **Setup Block Design:**
80123
* **Create Block Design** named design\_1.
81124
* **Add Zynq PS:** Add ZYNQ7 Processing System and run **Block Automation** (Apply Board Preset).
82125
* **Add GPIO:** Add AXI GPIO. Run **Connection Automation**.
83126
* *Select:* GPIO interface \-\> leds\_4bits (Board Interface).
84127
* *Note:* This maps the software "GPIO" directly to the physical LED pins.
85128
* **Add Custom IP:** Add axi\_dyn\_counter. Run **Connection Automation**.
86129
* Connects S00\_AXI to the Zynq GP Port.
87-
3. **Validate & Build:**
130+
* Connects S00\_AXI to the Zynq GP Port.
131+
4. **Validate & Build:**
88132
* **Validate Design** (F6).
89-
* **Create HDL Wrapper** (Right-click design\_1 in Sources).
133+
* **Create HDL Wrapper** (Right-click design\_1 in Sources-\>Design Sources).
90134
* **Generate Bitstream**.
135+
* Wait for it to finish with the message "Bitstream generated successfully". You can click **Cancel** in the dialog box.
91136
92137
### **Part C: The Hardware Handoff (.hwh)**
93138
@@ -113,55 +158,64 @@ Now we switch to the Jupyter Notebook interface.
113158
* Open Jupyter (http://pynq:9090).
114159
* Upload my\_counter.bit and my\_counter.hwh to the pynq home folder.
115160
2. Create Notebook:
116-
Create a new Python 3 notebook and run:
117-
```python
118-
from pynq import Overlay
119-
import time
120-
121-
# 1. Load the Overlay
122-
# PYNQ automatically parses the .hwh file to find our IP
123-
ol = Overlay("my_counter.bit")
124-
125-
# 2. Check available IPs
126-
# We should see 'axi_gpio_0' and 'axi_dyn_counter_0'
127-
print(ol.ip_dict.keys())
128-
129-
# 3. Create Drivers
130-
# Standard PYNQ driver for LEDs
131-
leds = ol.axi_gpio_0.channel1
132-
# Custom IP access via MMIO
133-
counter = ol.axi_dyn_counter_0
134-
135-
# 4. Test LEDs
136-
print("Blinking LEDs...")
137-
for i in range(4):
138-
leds.write(0xF, 0xF) # All ON
139-
time.sleep(0.2)
140-
leds.write(0xF, 0x0) # All OFF
141-
time.sleep(0.2)
142-
143-
# 5. Test Counter
144-
print("Testing Custom Counter...")
145-
146-
# Reset (Write 2 to Reg0)
147-
counter.write(0x0, 2)
148-
# Enable (Write 1 to Reg0)
149-
counter.write(0x0, 1)
150-
151-
print("Counting for 1 second...")
152-
time.sleep(1.0)
153-
154-
# Stop (Write 0 to Reg0)
155-
counter.write(0x0, 0)
156-
157-
# Read Result (Read Reg1 @ Offset 0x4)
158-
count_val = counter.read(0x4)
159-
160-
# Math check: Clock is 100MHz (standard Zynq FCLK).
161-
# 1 second should be ~100,000,000 ticks.
162-
print(f"Counter Value: {count_val}")
163-
print(f"Frequency: {count_val / 1_000_000:.2f} MHz")
164-
```
161+
Create a new Python 3 notebook and create then run the following cells:
162+
**Cell 1: Load Overlay & Create Drivers**
163+
```python
164+
from pynq import Overlay
165+
import time
166+
167+
# 1. Load the Overlay
168+
ol = Overlay("my_counter.bit")
169+
170+
# 2. Check IP Dictionary
171+
print("Available IPs:", ol.ip_dict.keys())
172+
173+
# 3. Create Drivers
174+
# Standard PYNQ driver for LEDs (High-Level)
175+
leds = ol.axi_gpio_0.channel1
176+
# Custom IP access via MMIO (Low-Level)
177+
counter = ol.axi_dyn_counter_0
178+
```
179+
180+
**Cell 2: Test LEDs (Standard GPIO)**
181+
```python
182+
# Configure Direction (TRI Register)
183+
# The High-Level driver defaults to Input. We must force Output.
184+
# Offset 0x4 = Channel 1 Tri-State Control (0 = Output, 1 = Input)
185+
ol.axi_gpio_0.mmio.write(0x4, 0x0)
186+
print("Direction set to Output via MMIO.")
187+
188+
print("Blinking LEDs...")
189+
for i in range(4):
190+
leds.write(0xF, 0xF) # All ON
191+
time.sleep(0.2)
192+
leds.write(0x0, 0xF) # All OFF
193+
time.sleep(0.2)
194+
print("Done.")
195+
```
196+
197+
**Cell 3: Test Custom Counter (MMIO)**
198+
```python
199+
print("Testing Custom Counter...")
200+
201+
# Reset (Write 2 to Reg0)
202+
counter.write(0x0, 2)
203+
# Enable (Write 1 to Reg0)
204+
counter.write(0x0, 1)
205+
206+
print("Counting for 1 second...")
207+
time.sleep(1.0)
208+
209+
# Stop (Write 0 to Reg0)
210+
counter.write(0x0, 0)
211+
212+
# Read Result (Read Reg1 @ Offset 0x4)
213+
count_val = counter.read(0x4)
214+
215+
# Math check: Clock is 100MHz. 1 second ~ 100M ticks.
216+
print(f"Counter Value: {count_val}")
217+
print(f"Frequency: {count_val / 1_000_000:.2f} MHz")
218+
```
165219

166220
3. **Expected Result:**
167221
* LEDs on the board blink.
@@ -177,6 +231,6 @@ Now we switch to the Jupyter Notebook interface.
177231

178232
## **6. Next Phase**
179233

180-
The hardware is done. Now we need to build the software brain that runs on top of it.
234+
The hardware is done. Now, let's look at where you go from here.
181235

182-
**[Go to Phase 4: The Embedded Linux Flow (OS Construction)](learning_phase4.md)**
236+
**[Go to Phase 4: Beyond the Basics (Graduation)](learning_phase4.md)**

0 commit comments

Comments
 (0)