vxkyyy commited on
Commit
92978e6
·
1 Parent(s): 59de470

feat: AgentIC v2.0 - Added strict Verilog syntax rules, auto-fixers, and Groq support. Added INSTALL.md for portability.

Browse files
INSTALL.md ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Installation & Portability Guide
2
+
3
+ Use this guide to set up **AgentIC** on a new machine.
4
+
5
+ ## 1. System Requirements
6
+ * **Operating System**: Linux (Ubuntu 20.04/22.04 LTS) or Windows with **WSL2** (Ubuntu).
7
+ * *Note: Pure Windows is not supported due to Electronic Design Automation (EDA) tool dependencies.*
8
+ * **Memory**: 8GB RAM minimum (16GB recommended for Physical Design).
9
+ * **Disk Space**: ~10GB (mostly for Docker images and PDKs).
10
+
11
+ ## 2. Core Dependencies
12
+ Install the required system tools before setting up the Python environment.
13
+
14
+ ### Ubuntu / Debian / WSL2:
15
+ ```bash
16
+ sudo apt update
17
+ sudo apt install -y git make python3 python3-venv python3-pip
18
+ sudo apt install -y iverilog build-essential
19
+ ```
20
+
21
+ ### Docker (Critical for OpenLane)
22
+ AgentIC uses OpenLane (running in Docker) to turn Verilog into GDSII layouts.
23
+ 1. **Install Docker Desktop** (Windows/Mac) or **Docker Engine** (Linux).
24
+ 2. **Verify installation**:
25
+ ```bash
26
+ docker run hello-world
27
+ ```
28
+ 3. **Linux/WSL2 users**: Ensure your user is in the docker group so you don't need `sudo`:
29
+ ```bash
30
+ sudo usermod -aG docker $USER
31
+ # Log out and log back in for this to take effect
32
+ ```
33
+
34
+ ## 3. Python Environment Setup
35
+
36
+ 1. **Clone the Repository**:
37
+ ```bash
38
+ git clone https://github.com/Vickyrrrrrr/AgentIC.git
39
+ cd AgentIC
40
+ ```
41
+
42
+ 2. **Create and Activate Virtual Environment**:
43
+ ```bash
44
+ python3 -m venv agentic_env
45
+ source agentic_env/bin/activate
46
+ ```
47
+
48
+ 3. **Install Python Dependencies**:
49
+ ```bash
50
+ pip install -r requirements.txt
51
+ ```
52
+
53
+ 4. **Install GDSTK (Layout Viewer)**:
54
+ If `pip install gdstk` fails, you may need cmake:
55
+ ```bash
56
+ sudo apt install cmake
57
+ pip install gdstk
58
+ ```
59
+
60
+ ## 4. Configuration (.env)
61
+
62
+ You need to provide your LLM API keys.
63
+ 1. Create a file named `.env` in the root `AgentIC` directory.
64
+ 2. Add your keys (example for Groq):
65
+ ```ini
66
+ # .env file
67
+ OPENAI_API_BASE=https://api.groq.com/openai/v1
68
+ OPENAI_API_KEY=gsk_your_groq_api_key_here
69
+ OPENAI_MODEL_NAME=llama-3.3-70b-versatile
70
+ ```
71
+
72
+ ## 5. Verification
73
+ To ensure everything is working:
74
+
75
+ 1. **Test the Agent Logic**:
76
+ ```bash
77
+ python3 main.py build --name test_counter --desc "A simple 4-bit up counter"
78
+ ```
79
+ 2. **Test the Web UI**:
80
+ ```bash
81
+ streamlit run app.py
82
+ ```
Makefile ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # AgentIC Makefile - Structured Verification & Hardening Workflow
2
+ # Usage: make <target> [DESIGN=name]
3
+
4
+ DESIGN ?= simple_counter
5
+ OPENLANE_ROOT ?= $(HOME)/OpenLane
6
+ DESIGN_SRC = designs/$(DESIGN)/src
7
+ RTL = $(DESIGN_SRC)/$(DESIGN).v
8
+ TB = $(DESIGN_SRC)/$(DESIGN)_tb.v
9
+ PDK_ROOT ?= $(HOME)/.ciel
10
+ OPENLANE_IMAGE ?= ghcr.io/the-openroad-project/openlane:ff5509f65b17bfa4068d5336495ab1718987ff69-amd64
11
+
12
+ .PHONY: all lint sim verify harden clean help
13
+
14
+ # Complete flow
15
+ all: lint sim harden verify
16
+ @echo "=========================================="
17
+ @echo " COMPLETE WORKFLOW FINISHED FOR $(DESIGN)"
18
+ @echo "=========================================="
19
+
20
+ # Step 1: Lint (syntax check)
21
+ lint:
22
+ @echo "━━━ [1/4] LINTING $(DESIGN) ━━━"
23
+ iverilog -t null $(RTL)
24
+ @echo "✓ Syntax OK"
25
+
26
+ # Step 2: Simulate (functional verification)
27
+ sim: lint
28
+ @echo "━━━ [2/4] SIMULATING $(DESIGN) ━━━"
29
+ iverilog -o $(DESIGN_SRC)/sim $(RTL) $(TB)
30
+ cd $(DESIGN_SRC) && vvp sim | tee sim.log
31
+ @grep -q "TEST PASSED" $(DESIGN_SRC)/sim.log && echo "✓ Simulation PASSED" || (echo "✗ Simulation FAILED" && exit 1)
32
+
33
+ # Step 3: Harden (RTL → GDSII via OpenLane Docker)
34
+ harden:
35
+ @echo "━━━ [3/4] HARDENING $(DESIGN) → GDSII ━━━"
36
+ @echo "Copying design to OpenLane..."
37
+ cp -r designs/$(DESIGN) $(OPENLANE_ROOT)/designs/
38
+ @echo "Running OpenLane flow (may take 5-15 min)..."
39
+ docker run --rm \
40
+ -v $(OPENLANE_ROOT):/openlane \
41
+ -v $(PDK_ROOT):$(PDK_ROOT) \
42
+ -e PDK_ROOT=$(PDK_ROOT) \
43
+ -e PDK=sky130A \
44
+ -e PWD=/openlane \
45
+ $(OPENLANE_IMAGE) \
46
+ ./flow.tcl -design $(DESIGN) -tag agent_run_$(shell date +%Y%m%d_%H%M%S) -ignore_mismatches
47
+ @echo "✓ Hardening complete. Check $(OPENLANE_ROOT)/designs/$(DESIGN)/runs/"
48
+
49
+ # Step 4: Verify (DRC/LVS)
50
+ verify:
51
+ @echo "━━━ [4/4] VERIFYING $(DESIGN) (DRC/LVS) ━━━"
52
+ bash scripts/verify_design.sh $(DESIGN)
53
+
54
+ # Utilities
55
+ clean:
56
+ rm -f $(DESIGN_SRC)/sim $(DESIGN_SRC)/sim.log $(DESIGN_SRC)/*.vcd
57
+ @echo "Cleaned simulation artifacts."
58
+
59
+ help:
60
+ @echo "AgentIC Makefile Targets:"
61
+ @echo " make lint - Check Verilog syntax"
62
+ @echo " make sim - Run RTL simulation"
63
+ @echo " make harden - Run OpenLane (RTL→GDSII)"
64
+ @echo " make verify - Run DRC/LVS checks"
65
+ @echo " make all - Run complete flow"
66
+ @echo " make clean - Remove temp files"
67
+ @echo ""
68
+ @echo "Override design: make DESIGN=my_counter sim"
README.md CHANGED
@@ -1,67 +1,97 @@
1
- # AgentIC Workflow Guide
2
 
3
- This document outlines the automated chip design workflow using **AgentIC** and **OpenLane**.
4
 
5
- ## Logic Flow
6
 
7
- The system transforms natural language descriptions into physical GDSII manufacturing files through a multi-step agentic pipeline.
 
 
 
 
 
 
 
 
 
8
 
9
- ```mermaid
10
- graph TD
11
- User[User Command] -->|main.py build| A[AgentIC]
12
- A -->|Step 1: Generated RTL| B(VLSI Design Agent)
13
- B -->|vicky_adder.v| C{Syntax Check}
14
- C -->|Pass| D(Verification Agent)
15
- C -->|Fail| Z[Error / Retry]
16
- D -->|vicky_adder_tb.v| E{Simulation}
17
- E -->|Pass| F[OpenLane Integration]
18
- E -->|Fail| Z
19
-
20
- subgraph OpenLane Infrastructure
21
- F -->|Docker Mount| G[OpenLane Flow]
22
- G -->|flow.tcl| H[Synthesis yosys]
23
- H --> I[Floorplan]
24
- I --> J[Placement]
25
- J --> K[Routing]
26
- K --> L[GDSII Layout]
27
- end
28
-
29
- L -->|Output| M[Final GDSII & Reports]
30
- ```
31
 
32
- ## Directory Structure & Important Files
33
 
34
- The workspace is consolidated into two main components:
35
 
36
- ### 1. AgentIC ( The "Brain" )
37
- Controls the process.
38
- - **`main.py`**: The entry point.
39
- - **`src/agentic/`**: The core Python package.
40
- - **`agents/`**: AI engineer definitions.
41
- - **`tools/`**: Interfaces to VLSI tools.
42
- - **`config.py`**: Configuration settings (paths, LLMs).
43
- - **`scripts/verify_design.sh`**: A helper script for verification.
44
-
45
- ### 2. OpenLane ( The "Muscle" )
46
- The execution engine.
47
- - **`designs/simple_counter`**: **CRITICAL**. Used as a configuration template for all new AI designs.
48
- - **`designs/<your_design>`**: Where your new chips will be created.
49
- - **`flow.tcl`**: The main script run by the Docker container.
50
 
51
- ## How to Run
 
 
 
 
52
 
53
- ### 1. Build a new chip
 
54
  ```bash
55
- cd ~/AgentIC
56
- python main.py build --name vicky_adder --desc "A 4-bit adder with carry out"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  ```
58
 
59
- ### 2. Verify an existing chip
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  ```bash
61
- cd ~/AgentIC
62
- python main.py verify vicky_adder
63
  ```
64
 
65
- ## Maintenance
66
- - **Do not delete** `OpenLane/designs/simple_counter` - it is needed for config generation.
67
- - **Do not delete** the `OpenLane` root files - they are mounted into the Docker container.
 
1
+ # AgentIC: Natural Language to Chip Layout (GDSII)
2
 
3
+ **AgentIC** is an automated AI Agent framework that transforms natural language descriptions into industry-standard physical chip layouts. It essentially acts as a "Text-to-Silicon" compiler, leveraging LLMs (DeepSeek, Llama 3, etc.) to write RTL, verify it, and drive the OpenLane physical design toolchain.
4
 
5
+ ## 🚀 Capabilities
6
 
7
+ * **Natural Language RTL**: Generates synthesizable **SystemVerilog** code based on your prompt.
8
+ * **Industry Standard Enforced**:
9
+ * Automatic usage of `logic`, `always_ff`, and `always_comb`.
10
+ * **Flattened I/O Ports**: Ensures compatibility with hardening tools.
11
+ * **Scalable Architecture**: Uses `parameters` for bus widths.
12
+ * **Self-Correcting Agents**:
13
+ * **Design Agent**: Writes the code.
14
+ * **Verification Agent**: Writes a self-checking Testbench (`_tb.v`).
15
+ * **Auto-Fix Loop**: If compilation or simulation fails, the agents read the error logs and patch the code automatically.
16
+ * **Physical Design (Hardening)**: Integrates directly with [OpenLane](https://github.com/The-OpenROAD-Project/OpenLane) to generate GDSII layouts.
17
 
18
+ ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
 
20
+ ## 🛠️ Workflow
21
 
22
+ The workflow consists of three main stages. You can run them all at once or individually.
23
 
24
+ ### 1. Build (Design & Verify)
25
+ This is the main entry point. It invites the AI to design the chip and verify it.
26
+ ```bash
27
+ python3 AgentIC/main.py build --name <design_name> --desc "<your description>"
28
+ ```
29
+ **Example:**
30
+ ```bash
31
+ python3 AgentIC/main.py build --name my_processor --desc "A 4x4 Systolic Array NPU with AXI Stream interface"
32
+ ```
33
+ * **Output**: Generates `src/<name>.v` and `src/<name>_tb.v`.
34
+ * **Action**: Runs syntax checks and simulations until `TEST PASSED` is confirmed.
 
 
 
35
 
36
+ ### 2. Simulate (Manual Verification)
37
+ If you have manually modified the Verilog files (or want to re-run verification without triggering the AI to overwrite your files), use this command.
38
+ ```bash
39
+ python3 AgentIC/main.py simulate --name <design_name>
40
+ ```
41
 
42
+ ### 3. Harden (RTL GDSII)
43
+ Once your Simulation passes, turn the Verilog into a physical layout using OpenLane. This step runs synthesis, placement, routing, and signoff checks.
44
  ```bash
45
+ python3 AgentIC/main.py harden --name <design_name>
46
+ ```
47
+ * **Output**: A `.gds` file in `OpenLane/designs/<name>/runs/.../results/final/gds/`.
48
+
49
+ ---
50
+
51
+ ## 📂 Project Structure
52
+
53
+ ```text
54
+ /home/vickynishad/
55
+ ├── AgentIC/ # The AI Core
56
+ │ ├── main.py # CLI Entry point
57
+ │ ├── .env # API Keys & Config
58
+ │ └── src/agentic/ # Source code for Agents & Tools
59
+
60
+ ├── OpenLane/ # Physical Design Engine
61
+ │ └── designs/
62
+ │ ├── simple_counter/ # Template configuration (DO NOT DELETE)
63
+ │ └── <your_design>/ # Generated chips go here
64
+ │ ├── config.tcl # Auto-generated OpenLane config
65
+ │ └── src/
66
+ │ ├── <name>.v # SystemVerilog RTL
67
+ │ └── <name>_tb.v # Testbench
68
  ```
69
 
70
+ ## 🔌 Setup & Prerequisites
71
+
72
+ 1. **Python 3.10+** & **Docker** (for OpenLane).
73
+ 2. **Icarus Verilog (`iverilog`)**.
74
+ 3. **LLM Configuration**:
75
+ You can use a local model (Ollama) or a Cloud API (Groq/DeepSeek) for faster inference.
76
+
77
+ Create a `.env` file in `AgentIC/`:
78
+ ```dotenv
79
+ # Option 1: Cloud (Recommended for Speed)
80
+ GROQ_API_KEY=gsk_...
81
+ LLM_MODEL=openai/llama-3.3-70b-versatile
82
+ LLM_BASE_URL=https://api.groq.com/openai/v1
83
+
84
+ # Option 2: Local (DeepSeek R1 via Ollama)
85
+ # LLM_MODEL=ollama/deepseek-r1
86
+ # LLM_BASE_URL=http://localhost:11434
87
+ ```
88
+
89
+ ## 🧠 AI Reasoning
90
+ By default, if the model supports "Chain of Thought" (like DeepSeek R1), the tool can show the hidden reasoning process.
91
  ```bash
92
+ python3 AgentIC/main.py build ... --show-thinking
 
93
  ```
94
 
95
+ ---
96
+ **Author**: Vickyrrrrrr
97
+ **Powered by**: CrewAI & OpenLane
app.py CHANGED
@@ -412,7 +412,7 @@ elif selected_page == "Design Studio":
412
  cmd = ["python3", "AgentIC/main.py", "build", "--name", name.strip().replace(" ", "_"), "--desc", desc]
413
 
414
  with st.status("🤖 AgentIC is planning silicon...", expanded=True) as status:
415
- st.write("1. Initializing DeepSeek-R1 Agent...")
416
  st.write("2. Generating RTL Logic...")
417
 
418
  try:
@@ -490,7 +490,7 @@ elif selected_page == "Design Studio":
490
  st.markdown(f"""
491
  <div class="terminal-window">
492
  [SYSTEM] Initialized AgentIC Kernel v2.0.<br>
493
- [INFO] DeepSeek-R1 Model Loaded (Quantized).<br>
494
  [INFO] Connected to OpenLane Docker Container.<br>
495
  <span style="color:#00D1FF">vickynishad@agentic:~$</span> Waiting for command...
496
  </div>
@@ -845,6 +845,6 @@ elif selected_page == "Fabrication":
845
  st.markdown("---")
846
  st.markdown("""
847
  <div style="text-align: center; color: #555; font-size: 12px;">
848
- AGENTIC FRAMEWORK © 2026 | POWERED BY DEEPSEEK & OPENLANE
849
  </div>
850
  """, unsafe_allow_html=True)
 
412
  cmd = ["python3", "AgentIC/main.py", "build", "--name", name.strip().replace(" ", "_"), "--desc", desc]
413
 
414
  with st.status("🤖 AgentIC is planning silicon...", expanded=True) as status:
415
+ st.write("1. Initializing AI Agent...")
416
  st.write("2. Generating RTL Logic...")
417
 
418
  try:
 
490
  st.markdown(f"""
491
  <div class="terminal-window">
492
  [SYSTEM] Initialized AgentIC Kernel v2.0.<br>
493
+ [INFO] AI Model Loaded.<br>
494
  [INFO] Connected to OpenLane Docker Container.<br>
495
  <span style="color:#00D1FF">vickynishad@agentic:~$</span> Waiting for command...
496
  </div>
 
845
  st.markdown("---")
846
  st.markdown("""
847
  <div style="text-align: center; color: #555; font-size: 12px;">
848
+ AGENTIC FRAMEWORK © 2026 | POWERED BY LLM & OPENLANE
849
  </div>
850
  """, unsafe_allow_html=True)
debug_llm.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+
4
+ # Add src to path to import config
5
+ sys.path.append(os.path.join(os.path.dirname(__file__), 'src'))
6
+ from agentic.config import LLM_MODEL, LLM_BASE_URL, LLM_API_KEY
7
+ from crewai import LLM
8
+
9
+ print(f"--- Debugging LLM Configuration ---")
10
+ print(f"Model: {LLM_MODEL}")
11
+ print(f"Base URL: {LLM_BASE_URL}")
12
+ print(f"API Key: {'*' * 5}{LLM_API_KEY[-4:] if LLM_API_KEY and len(LLM_API_KEY) > 4 else 'NA'}")
13
+
14
+ print(f"\n--- Sending Test Prompt to '{LLM_MODEL}' ---")
15
+
16
+ try:
17
+ llm = LLM(
18
+ model=LLM_MODEL,
19
+ base_url=LLM_BASE_URL,
20
+ api_key=LLM_API_KEY
21
+ )
22
+ # Simple direct generation check
23
+ resp = llm.call(
24
+ messages=[{"role": "user", "content": "Hello! Are you ready to design chips?"}]
25
+ )
26
+ print(f"✅ Response received:\n{resp}")
27
+
28
+ except Exception as e:
29
+ print(f"❌ Error contacting model: {e}")
designs/simple_counter/config.tcl ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ set ::env(DESIGN_NAME) "simple_counter"
2
+ set ::env(VERILOG_FILES) "$::env(DESIGN_DIR)/src/simple_counter.v"
3
+
4
+ # Die size 200um x 200um
5
+ set ::env(DIE_AREA) "0 0 200 200"
6
+ set ::env(FP_SIZING) absolute
7
+
8
+ # Core utilization
9
+ set ::env(FP_CORE_UTIL) 20
10
+ set ::env(PL_TARGET_DENSITY) 0.3
11
+
12
+ # Power Grid Settings
13
+ set ::env(FP_PDN_VPITCH) 50
14
+ set ::env(FP_PDN_HPITCH) 50
15
+ set ::env(FP_PDN_VOFFSET) 5
16
+ set ::env(FP_PDN_HOFFSET) 5
17
+
18
+ # Power Pins / Voltage Sources
19
+ set ::env(VDD_NETS) [list {vccd1}]
20
+ set ::env(GND_NETS) [list {vssd1}]
21
+ set ::env(SYNTH_USE_PG_PINS_DEFINES) "USE_POWER_PINS"
22
+
23
+ # Technology Setup
24
+ set ::env(PDK) "sky130A"
25
+ set ::env(STD_CELL_LIBRARY) "sky130_fd_sc_hd"
26
+ set ::env(CLOCK_PORT) "clk"
27
+ set ::env(CLOCK_PERIOD) "10.0"
designs/simple_counter/src/sim ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #! /usr/bin/vvp
2
+ :ivl_version "11.0 (stable)";
3
+ :ivl_delay_selection "TYPICAL";
4
+ :vpi_time_precision - 12;
5
+ :vpi_module "/usr/lib/x86_64-linux-gnu/ivl/system.vpi";
6
+ :vpi_module "/usr/lib/x86_64-linux-gnu/ivl/vhdl_sys.vpi";
7
+ :vpi_module "/usr/lib/x86_64-linux-gnu/ivl/vhdl_textio.vpi";
8
+ :vpi_module "/usr/lib/x86_64-linux-gnu/ivl/v2005_math.vpi";
9
+ :vpi_module "/usr/lib/x86_64-linux-gnu/ivl/va_math.vpi";
10
+ S_0x5ed6c51adfd0 .scope module, "simple_counter_tb" "simple_counter_tb" 2 3;
11
+ .timescale -9 -12;
12
+ v0x5ed6c51c28a0_0 .var "clk", 0 0;
13
+ v0x5ed6c51c2960_0 .net "count", 7 0, v0x5ed6c51c2580_0; 1 drivers
14
+ v0x5ed6c51c2a30_0 .var "enable", 0 0;
15
+ v0x5ed6c51c2b30_0 .var "rst_n", 0 0;
16
+ S_0x5ed6c51ae160 .scope module, "uut" "simple_counter" 2 14, 3 1 0, S_0x5ed6c51adfd0;
17
+ .timescale 0 0;
18
+ .port_info 0 /INPUT 1 "clk";
19
+ .port_info 1 /INPUT 1 "rst_n";
20
+ .port_info 2 /INPUT 1 "enable";
21
+ .port_info 3 /OUTPUT 8 "count";
22
+ v0x5ed6c5175ba0_0 .net "clk", 0 0, v0x5ed6c51c28a0_0; 1 drivers
23
+ v0x5ed6c51c2580_0 .var "count", 7 0;
24
+ v0x5ed6c51c2660_0 .net "enable", 0 0, v0x5ed6c51c2a30_0; 1 drivers
25
+ v0x5ed6c51c2730_0 .net "rst_n", 0 0, v0x5ed6c51c2b30_0; 1 drivers
26
+ E_0x5ed6c51acc50/0 .event negedge, v0x5ed6c51c2730_0;
27
+ E_0x5ed6c51acc50/1 .event posedge, v0x5ed6c5175ba0_0;
28
+ E_0x5ed6c51acc50 .event/or E_0x5ed6c51acc50/0, E_0x5ed6c51acc50/1;
29
+ .scope S_0x5ed6c51ae160;
30
+ T_0 ;
31
+ %wait E_0x5ed6c51acc50;
32
+ %load/vec4 v0x5ed6c51c2730_0;
33
+ %nor/r;
34
+ %flag_set/vec4 8;
35
+ %jmp/0xz T_0.0, 8;
36
+ %pushi/vec4 0, 0, 8;
37
+ %assign/vec4 v0x5ed6c51c2580_0, 0;
38
+ %jmp T_0.1;
39
+ T_0.0 ;
40
+ %load/vec4 v0x5ed6c51c2660_0;
41
+ %flag_set/vec4 8;
42
+ %jmp/0xz T_0.2, 8;
43
+ %load/vec4 v0x5ed6c51c2580_0;
44
+ %addi 1, 0, 8;
45
+ %assign/vec4 v0x5ed6c51c2580_0, 0;
46
+ T_0.2 ;
47
+ T_0.1 ;
48
+ %jmp T_0;
49
+ .thread T_0;
50
+ .scope S_0x5ed6c51adfd0;
51
+ T_1 ;
52
+ %pushi/vec4 0, 0, 1;
53
+ %store/vec4 v0x5ed6c51c28a0_0, 0, 1;
54
+ T_1.0 ;
55
+ %delay 5000, 0;
56
+ %load/vec4 v0x5ed6c51c28a0_0;
57
+ %inv;
58
+ %store/vec4 v0x5ed6c51c28a0_0, 0, 1;
59
+ %jmp T_1.0;
60
+ %end;
61
+ .thread T_1;
62
+ .scope S_0x5ed6c51adfd0;
63
+ T_2 ;
64
+ %pushi/vec4 0, 0, 1;
65
+ %store/vec4 v0x5ed6c51c2b30_0, 0, 1;
66
+ %pushi/vec4 0, 0, 1;
67
+ %store/vec4 v0x5ed6c51c2a30_0, 0, 1;
68
+ %vpi_call 2 34 "$monitor", "Time=%0t | rst_n=%b | enable=%b | count=%d", $time, v0x5ed6c51c2b30_0, v0x5ed6c51c2a30_0, v0x5ed6c51c2960_0 {0 0 0};
69
+ %delay 20000, 0;
70
+ %pushi/vec4 1, 0, 1;
71
+ %store/vec4 v0x5ed6c51c2b30_0, 0, 1;
72
+ %delay 20000, 0;
73
+ %pushi/vec4 1, 0, 1;
74
+ %store/vec4 v0x5ed6c51c2a30_0, 0, 1;
75
+ %delay 200000, 0;
76
+ %pushi/vec4 0, 0, 1;
77
+ %store/vec4 v0x5ed6c51c2a30_0, 0, 1;
78
+ %delay 50000, 0;
79
+ %pushi/vec4 0, 0, 1;
80
+ %store/vec4 v0x5ed6c51c2b30_0, 0, 1;
81
+ %delay 20000, 0;
82
+ %vpi_call 2 53 "$display", "TEST PASSED" {0 0 0};
83
+ %vpi_call 2 54 "$finish" {0 0 0};
84
+ %end;
85
+ .thread T_2;
86
+ .scope S_0x5ed6c51adfd0;
87
+ T_3 ;
88
+ %vpi_call 2 59 "$dumpfile", "simple_counter.vcd" {0 0 0};
89
+ %vpi_call 2 60 "$dumpvars", 32'sb00000000000000000000000000000000, S_0x5ed6c51adfd0 {0 0 0};
90
+ %end;
91
+ .thread T_3;
92
+ # The file index is used to find the file name in the following table.
93
+ :file_names 4;
94
+ "N/A";
95
+ "<interactive>";
96
+ "designs/simple_counter/src/simple_counter_tb.v";
97
+ "designs/simple_counter/src/simple_counter.v";
designs/simple_counter/src/simple_counter.v ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ module simple_counter (
2
+ input wire clk,
3
+ input wire rst_n,
4
+ input wire enable,
5
+ output reg [7:0] count
6
+ );
7
+
8
+ always @(posedge clk or negedge rst_n) begin
9
+ if (!rst_n) begin
10
+ count <= 8'b0;
11
+ end else if (enable) begin
12
+ count <= count + 1'b1;
13
+ end
14
+ end
15
+
16
+ endmodule
designs/simple_counter/src/simple_counter_tb.v ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ `timescale 1ns / 1ps
2
+
3
+ module simple_counter_tb;
4
+
5
+ // Inputs
6
+ reg clk;
7
+ reg rst_n;
8
+ reg enable;
9
+
10
+ // Outputs
11
+ wire [7:0] count;
12
+
13
+ // Instantiate the Unit Under Test (UUT)
14
+ simple_counter uut (
15
+ .clk(clk),
16
+ .rst_n(rst_n),
17
+ .enable(enable),
18
+ .count(count)
19
+ );
20
+
21
+ // Clock generation
22
+ initial begin
23
+ clk = 0;
24
+ forever #5 clk = ~clk; // 100MHz clock
25
+ end
26
+
27
+ // Test Sequence
28
+ initial begin
29
+ // Initialize Inputs
30
+ rst_n = 0;
31
+ enable = 0;
32
+
33
+ // Monitoring
34
+ $monitor("Time=%0t | rst_n=%b | enable=%b | count=%d", $time, rst_n, enable, count);
35
+
36
+ // Reset
37
+ #20;
38
+ rst_n = 1;
39
+ #20;
40
+
41
+ // Enable Counter
42
+ enable = 1;
43
+ #200;
44
+
45
+ // Disable Counter
46
+ enable = 0;
47
+ #50;
48
+
49
+ // Reset again
50
+ rst_n = 0;
51
+ #20;
52
+
53
+ $display("TEST PASSED");
54
+ $finish;
55
+ end
56
+
57
+ // Waveform dump for GTKWave
58
+ initial begin
59
+ $dumpfile("simple_counter.vcd");
60
+ $dumpvars(0, simple_counter_tb);
61
+ end
62
+
63
+ endmodule
main.py CHANGED
@@ -1,6 +1,10 @@
1
  #!/usr/bin/env python3
2
  import sys
3
  import os
 
 
 
 
4
 
5
  # Add src to path so we can import the package
6
  sys.path.insert(0, os.path.join(os.path.dirname(__file__), "src"))
 
1
  #!/usr/bin/env python3
2
  import sys
3
  import os
4
+ from dotenv import load_dotenv
5
+
6
+ # Load environment variables from .env file
7
+ load_dotenv()
8
 
9
  # Add src to path so we can import the package
10
  sys.path.insert(0, os.path.join(os.path.dirname(__file__), "src"))
scripts/verify_design.sh CHANGED
@@ -8,7 +8,7 @@ if [ -z "$1" ]; then
8
  fi
9
 
10
  DESIGN=$1
11
- OPENLANE_ROOT=~/OpenLane
12
  DESIGN_DIR=$OPENLANE_ROOT/designs/$DESIGN
13
 
14
  echo "========================================"
 
8
  fi
9
 
10
  DESIGN=$1
11
+ OPENLANE_ROOT="${OPENLANE_ROOT:-$HOME/OpenLane}"
12
  DESIGN_DIR=$OPENLANE_ROOT/designs/$DESIGN
13
 
14
  echo "========================================"
src/agentic/agents/designer.py CHANGED
@@ -1,14 +1,12 @@
1
  # agents/designer.py
2
  from crewai import Agent
3
- from ..config import LLM_MODEL
4
 
5
- def get_designer_agent():
6
  return Agent(
7
  role='VLSI Design Engineer',
8
- goal='Create optimized Verilog for {design_input}',
9
- backstory='Specialist in ECE from Lucknow University. Expert in Sky130 PDK.',
10
- # IMPORTANT: Use the "ollama/" prefix so CrewAI doesn't look for OpenAI
11
- llm=LLM_MODEL,
12
- verbose=True,
13
  allow_delegation=False
14
  )
 
1
  # agents/designer.py
2
  from crewai import Agent
 
3
 
4
+ def get_designer_agent(llm, goal, verbose=False):
5
  return Agent(
6
  role='VLSI Design Engineer',
7
+ goal=goal,
8
+ backstory='Specialist in ECE from Lucknow University. Expert in digital design with Sky130 PDK experience.',
9
+ llm=llm,
10
+ verbose=verbose,
 
11
  allow_delegation=False
12
  )
src/agentic/agents/testbench_designer.py CHANGED
@@ -1,15 +1,15 @@
1
  # agents/testbench_designer.py
2
  from crewai import Agent
3
 
4
- def get_testbench_agent(llm):
5
  """Returns an agent specialized in writing Verilog testbenches."""
6
  return Agent(
7
  role='Verification Engineer',
8
- goal='Create comprehensive Verilog testbenches for {design_name}',
9
  backstory='''Expert in digital verification with deep knowledge of
10
  Verilog simulation. Focuses on edge cases, reset behavior, and
11
  timing checks. Always includes $monitor, $dumpfile, and $finish.''',
12
  llm=llm,
13
- verbose=True,
14
  allow_delegation=False
15
  )
 
1
  # agents/testbench_designer.py
2
  from crewai import Agent
3
 
4
+ def get_testbench_agent(llm, goal, verbose=False):
5
  """Returns an agent specialized in writing Verilog testbenches."""
6
  return Agent(
7
  role='Verification Engineer',
8
+ goal=goal,
9
  backstory='''Expert in digital verification with deep knowledge of
10
  Verilog simulation. Focuses on edge cases, reset behavior, and
11
  timing checks. Always includes $monitor, $dumpfile, and $finish.''',
12
  llm=llm,
13
+ verbose=verbose,
14
  allow_delegation=False
15
  )
src/agentic/cli.py CHANGED
@@ -2,13 +2,15 @@
2
  """
3
  AgentIC - Natural Language to GDSII Pipeline
4
  =============================================
5
- Uses CrewAI + Ollama (DeepSeek) to generate chips from natural language.
 
6
 
7
  Usage:
8
  python main.py build --name counter --desc "8-bit counter with enable and reset"
9
  """
10
 
11
  import os
 
12
  import sys
13
  import typer
14
  from rich.console import Console
@@ -18,6 +20,8 @@ from crewai import Agent, Task, Crew, LLM
18
 
19
  # Local imports
20
  from .config import OPENLANE_ROOT, LLM_MODEL, LLM_BASE_URL, LLM_API_KEY
 
 
21
  from .tools.vlsi_tools import (
22
  write_verilog,
23
  run_syntax_check,
@@ -39,11 +43,83 @@ def get_llm():
39
  api_key=LLM_API_KEY
40
  )
41
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  # --- THE BUILD COMMAND ---
43
  @app.command()
44
  def build(
45
  name: str = typer.Option(..., "--name", "-n", help="Design name (e.g., counter)"),
46
- desc: str = typer.Option(..., "--desc", "-d", help="Natural language description")
 
 
 
47
  ):
48
  """Build a chip from natural language description."""
49
 
@@ -55,156 +131,366 @@ def build(
55
  ))
56
 
57
  llm = get_llm()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
 
59
- # ===== STEP 1: Generate RTL =====
60
  console.print("\n[bold yellow]━━━ Step 1/5: Generating Verilog RTL ━━━[/bold yellow]")
61
-
62
- rtl_agent = Agent(
63
- role='VLSI Design Engineer',
64
- goal=f'Create synthesizable Verilog for: {desc}',
65
- backstory='Expert in digital design with Sky130 PDK experience.',
66
  llm=llm,
67
- verbose=False
 
68
  )
69
-
70
  rtl_task = Task(
71
- description=f'''Design a Verilog module named "{name}" that implements: {desc}
72
-
73
- CRITICAL REQUIREMENTS:
74
- - Module name must be exactly "{name}"
75
- - Use VERILOG-2005 ONLY (NOT SystemVerilog)
76
- - Use "always @(*)" NOT "always_comb"
77
- - Use "reg" for outputs in combinational logic
78
- - Include clock (clk) and active-low reset (rst_n) if sequential
79
- - Return ONLY the Verilog code wrapped in ```verilog fences
80
- - NO explanations, NO comments about the code
81
-
82
- Example format:
83
- ```verilog
84
- module {name}(...);
85
- ...
86
- endmodule
87
- ```
88
- ''',
89
- expected_output='Complete Verilog-2005 module code in markdown fence',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  agent=rtl_agent
91
  )
92
-
93
- with console.status("[cyan]AI is designing your chip...[/cyan]"):
94
  rtl_result = Crew(agents=[rtl_agent], tasks=[rtl_task]).kickoff()
95
-
96
- rtl_path = write_verilog(name, str(rtl_result))
 
 
 
97
  console.print(f" ✓ RTL saved to: [green]{rtl_path}[/green]")
98
-
99
- # ===== STEP 2: Syntax Check RTL =====
100
  console.print("\n[bold yellow]━━━ Step 2/5: Syntax Verification ━━━[/bold yellow]")
101
-
102
  success, errors = run_syntax_check(rtl_path)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
  if not success:
104
  console.print(f"[bold red]✗ SYNTAX ERROR:[/bold red]\n{errors}")
105
  raise typer.Exit(1)
106
  console.print(" ✓ Verilog syntax is [green]valid[/green]")
107
 
108
- # ===== STEP 3: Generate Testbench =====
109
  console.print("\n[bold yellow]━━━ Step 3/5: Generating Testbench ━━━[/bold yellow]")
110
-
111
- # Read the generated RTL for context
112
  with open(rtl_path, 'r') as f:
113
  rtl_code = f.read()
114
-
115
- tb_agent = Agent(
116
- role='Verification Engineer',
117
- goal=f'Create a testbench for {name}',
118
- backstory='Expert in Verilog verification and simulation.',
119
  llm=llm,
120
- verbose=False
 
121
  )
122
-
123
  tb_task = Task(
124
- description=f'''Create a Verilog-2005 testbench for this module:
125
-
126
  ```verilog
127
  {rtl_code}
128
  ```
129
 
130
- CRITICAL REQUIREMENTS:
131
- - Module name must be "{name}_tb"
132
- - Use VERILOG-2005 ONLY (NOT SystemVerilog)
133
- - Use explicit port connections: .clk(clk), .a(a), etc.
134
- - Use integer delays like #5 or #10, NOT #5ns or #10ns
135
- - Clock: forever #5 clk = ~clk (inside initial block)
136
- - Put $monitor INSIDE an initial block
137
- - Use $dumpfile("{name}.vcd") and $dumpvars(0, {name}_tb)
138
- - End with $finish
139
- - Return ONLY the testbench code wrapped in ```verilog fences
140
-
141
- Example structure:
142
- ```verilog
143
- module {name}_tb;
144
- reg clk, rst_n;
145
- // declare inputs as reg, outputs as wire
146
-
147
- {name} uut(.clk(clk), .rst_n(rst_n), ...);
148
-
149
- initial begin
150
- clk = 0;
151
- forever #5 clk = ~clk;
152
- end
153
-
154
- initial begin
155
- $dumpfile("{name}.vcd");
156
- $dumpvars(0, {name}_tb);
157
- $monitor(...);
158
- // test sequence
159
- $finish;
160
- end
161
- endmodule
162
- ```
163
  ''',
164
- expected_output='Complete Verilog-2005 testbench in markdown fence',
165
  agent=tb_agent
166
  )
167
-
168
  with console.status("[cyan]AI is creating testbench...[/cyan]"):
169
  tb_result = Crew(agents=[tb_agent], tasks=[tb_task]).kickoff()
170
-
171
- tb_path = write_verilog(name, str(tb_result), is_testbench=True)
 
 
 
172
  console.print(f" ✓ Testbench saved to: [green]{tb_path}[/green]")
173
 
174
- # ===== STEP 4: Run Simulation =====
175
  console.print("\n[bold yellow]━━━ Step 4/5: Running Simulation ━━━[/bold yellow]")
176
-
177
  sim_success, sim_output = run_simulation(name)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
  if not sim_success:
179
  console.print(f"[bold red]✗ SIMULATION FAILED:[/bold red]\n{sim_output}")
180
  raise typer.Exit(1)
181
-
182
- # Show last few lines of simulation
183
  sim_lines = sim_output.strip().split('\n')
184
- for line in sim_lines[-5:]:
185
  console.print(f" [dim]{line}[/dim]")
186
  console.print(" ✓ Simulation [green]passed[/green]")
187
 
 
 
 
 
188
  # ===== STEP 5: Run OpenLane =====
189
  console.print("\n[bold yellow]━━━ Step 5/5: Running OpenLane (RTL → GDSII) ━━━[/bold yellow]")
190
  console.print(" [dim]This may take 5-10 minutes...[/dim]")
191
 
192
- # Create config.tcl from template
193
  template_config = f"{OPENLANE_ROOT}/designs/simple_counter/config.tcl"
194
  new_config = f"{OPENLANE_ROOT}/designs/{name}/config.tcl"
195
-
196
- if os.path.exists(template_config) and not os.path.exists(new_config):
197
  with open(template_config, 'r') as f:
198
  content = f.read().replace("simple_counter", name)
199
- # Fix: Ensure we only synthesize the design, not the testbench
200
- if "[glob $::env(DESIGN_DIR)/src/*.v]" in content:
201
- content = content.replace(
202
- "[glob $::env(DESIGN_DIR)/src/*.v]",
203
- f'"$::env(DESIGN_DIR)/src/{name}.v"'
204
- )
 
 
 
 
 
 
 
205
  with open(new_config, 'w') as f:
206
  f.write(content)
207
- console.print(f" ✓ Config created from template")
 
 
 
208
 
209
  ol_success, ol_result = run_openlane(name)
210
 
 
2
  """
3
  AgentIC - Natural Language to GDSII Pipeline
4
  =============================================
5
+ Uses CrewAI + LLM (DeepSeek/Llama/Groq) to generate chips from natural language.
6
+
7
 
8
  Usage:
9
  python main.py build --name counter --desc "8-bit counter with enable and reset"
10
  """
11
 
12
  import os
13
+ import re
14
  import sys
15
  import typer
16
  from rich.console import Console
 
20
 
21
  # Local imports
22
  from .config import OPENLANE_ROOT, LLM_MODEL, LLM_BASE_URL, LLM_API_KEY
23
+ from .agents.designer import get_designer_agent
24
+ from .agents.testbench_designer import get_testbench_agent
25
  from .tools.vlsi_tools import (
26
  write_verilog,
27
  run_syntax_check,
 
43
  api_key=LLM_API_KEY
44
  )
45
 
46
+ @app.command()
47
+ def simulate(
48
+ name: str = typer.Option(..., "--name", "-n", help="Design name (e.g., counter)"),
49
+ ):
50
+ """Run simulation on an existing design without regenerating RTL."""
51
+ console.print(Panel(
52
+ f"[bold cyan]AgentIC: Manual Simulation Mode[/bold cyan]\n"
53
+ f"Design: [yellow]{name}[/yellow]",
54
+ title="🚀 Starting Simulation"
55
+ ))
56
+
57
+ sim_success, sim_output = run_simulation(name)
58
+
59
+ if not sim_success:
60
+ console.print(f"[bold red]✗ SIMULATION FAILED:[/bold red]\n{sim_output}")
61
+ raise typer.Exit(1)
62
+
63
+ sim_lines = sim_output.strip().split('\n')
64
+ for line in sim_lines[-20:]: # Print last 20 lines of log
65
+ console.print(f" [dim]{line}[/dim]")
66
+ console.print(" ✓ Simulation [green]passed[/green]")
67
+
68
+ @app.command()
69
+ def harden(
70
+ name: str = typer.Option(..., "--name", "-n", help="Design name (e.g., counter)"),
71
+ ):
72
+ """Run OpenLane hardening (RTL -> GDSII) on an existing design."""
73
+ console.print(Panel(
74
+ f"[bold cyan]AgentIC: Manual Hardening Mode[/bold cyan]\n"
75
+ f"Design: [yellow]{name}[/yellow]",
76
+ title="🚀 Starting OpenLane"
77
+ ))
78
+
79
+ # Check config first
80
+ template_config = f"{OPENLANE_ROOT}/designs/simple_counter/config.tcl"
81
+ new_config = f"{OPENLANE_ROOT}/designs/{name}/config.tcl"
82
+
83
+ if not os.path.exists(new_config):
84
+ if os.path.exists(template_config):
85
+ with open(template_config, 'r') as f:
86
+ content = f.read().replace("simple_counter", name)
87
+
88
+ content = content.replace(
89
+ "set ::env(VERILOG_FILES) [glob $::env(DESIGN_DIR)/src/*.v]",
90
+ f"set ::env(VERILOG_FILES) \"$::env(DESIGN_DIR)/src/{name}.v\""
91
+ )
92
+ content = content.replace(
93
+ "set ::env(VERILOG_FILES) \"$::env(DESIGN_DIR)/src/simple_counter.v\"",
94
+ f"set ::env(VERILOG_FILES) \"$::env(DESIGN_DIR)/src/{name}.v\""
95
+ )
96
+
97
+ os.makedirs(os.path.dirname(new_config), exist_ok=True)
98
+ with open(new_config, 'w') as f:
99
+ f.write(content)
100
+ console.print(f" ✓ Config written: [green]{new_config}[/green]")
101
+ else:
102
+ console.print(f"[bold red]✗ Config not found and template missing.[/bold red]")
103
+ raise typer.Exit(1)
104
+
105
+ console.print(" [dim]Running OpenLane (this may take 5-10 minutes)...[/dim]")
106
+ ol_success, ol_result = run_openlane(name)
107
+
108
+ if ol_success:
109
+ console.print(f" ✓ GDSII generated: [green]{ol_result}[/green]")
110
+ else:
111
+ console.print(f"[bold red]✗ OpenLane failed[/bold red]")
112
+ console.print(f" Error: {ol_result[:500]}...")
113
+ raise typer.Exit(1)
114
+
115
  # --- THE BUILD COMMAND ---
116
  @app.command()
117
  def build(
118
  name: str = typer.Option(..., "--name", "-n", help="Design name (e.g., counter)"),
119
+ desc: str = typer.Option(..., "--desc", "-d", help="Natural language description"),
120
+ max_retries: int = typer.Option(2, "--max-retries", "-r", min=0, help="Max auto-fix retries for RTL/TB/sim failures"),
121
+ skip_openlane: bool = typer.Option(False, "--skip-openlane", help="Stop after simulation (no RTL→GDSII hardening)"),
122
+ show_thinking: bool = typer.Option(True, "--show-thinking", help="Print DeepSeek <think> reasoning for each generation/fix step")
123
  ):
124
  """Build a chip from natural language description."""
125
 
 
131
  ))
132
 
133
  llm = get_llm()
134
+
135
+ def log_thinking(raw_text: str, step: str):
136
+ """Emit DeepSeek <think> content to the console when requested."""
137
+ if not show_thinking or "<think>" not in raw_text:
138
+ return
139
+ thoughts = re.findall(r"<think>(.*?)</think>", raw_text, flags=re.DOTALL)
140
+ cleaned = [t.strip() for t in thoughts if t.strip()]
141
+ if not cleaned:
142
+ return
143
+ console.print(Panel("\n\n".join(cleaned), title=f"🧠 DeepSeek thinking — {step}", expand=False))
144
+
145
+ def _fix_with_llm(agent_role: str, goal: str, prompt: str) -> str:
146
+ fix_agent = Agent(
147
+ role=agent_role,
148
+ goal=goal,
149
+ backstory='Expert in SystemVerilog and ASIC flows (Sky130/OpenLane).',
150
+ llm=llm,
151
+ verbose=show_thinking
152
+ )
153
+
154
+ # Add explicit instruction to avoid dynamic variable names in loops
155
+ enhanced_prompt = prompt + "\n\nCRITICAL RULE: Do NOT use variable names like 'input_i' or 'wire_j' inside loops. SystemVerilog does not support dynamic variable names. Use arrays (e.g., input[i]) or generate blocks instead."
156
+
157
+ fix_task = Task(
158
+ description=enhanced_prompt,
159
+ expected_output='Corrected SystemVerilog code in a ```verilog fence',
160
+ agent=fix_agent
161
+ )
162
+ with console.status("[cyan]AI is fixing the design...[/cyan]"):
163
+ result = str(Crew(agents=[fix_agent], tasks=[fix_task]).kickoff())
164
+ log_thinking(result, step=f"Fix: {agent_role}")
165
+ return result
166
 
167
+ # ===== STEP 1/2: Generate RTL with syntax-fix loop =====
168
  console.print("\n[bold yellow]━━━ Step 1/5: Generating Verilog RTL ━━━[/bold yellow]")
169
+
170
+ rtl_agent = get_designer_agent(
 
 
 
171
  llm=llm,
172
+ goal=f'Create synthesizable SystemVerilog for: {desc}',
173
+ verbose=show_thinking
174
  )
175
+
176
  rtl_task = Task(
177
+ description=f'''Design an INDUSTRY STANDARD SystemVerilog module named "{name}" that implements: {desc}
178
+
179
+ CRITICAL VERILOG RULES (STRICTLY FOLLOW OR COMPILATION WILL FAIL):
180
+ 1. **Module & Ports**:
181
+ - Module name must be exactly "{name}"
182
+ - ALWAYS include `input logic clk`, `input logic rst_n` (active-low asynchronous reset).
183
+ - **FLATTENED PORTS**: Do not use multidimensional arrays for top-level ports. Use `input logic [WIDTH*COUNT-1:0] flat_bus`.
184
+ - Use `parameter` for all bit-widths.
185
+
186
+ 2. **Data Types & Declarations**:
187
+ - Use `logic` for ALL internal signals and ports (Standard SystemVerilog 2012).
188
+ - **DECLARE BEFORE USE**: You MUST declare signals (`logic [31:0] my_sig;`) at the top of the module interaction section, BEFORE they are used in any `always` block or `assign`.
189
+
190
+ 3. **Coding Logic Structure**:
191
+ - **SEPARATE LOGIC**:
192
+ - Use `always_ff @(posedge clk or negedge rst_n)` ONLY for registers/flip-flops.
193
+ - Use `assign` statements for renaming wires or simple math (e.g. `assign opcode = ir[6:0];`).
194
+ - Use `always_comb` ONLY for complex muxing/Next-State-Logic.
195
+ - **NO CONSTANT SELECTS IN ALWAYS**: Do NOT do `reg_file[ ir[19:15] ]` inside an `always` block.
196
+ - CORRECT:
197
+ ```verilog
198
+ wire [4:0] rs1_addr = ir[19:15]; // Create alias wire using assign
199
+ ...
200
+ always_comb val = reg_file[rs1_addr];
201
+ ```
202
+ - **LOOPS**: For register files, declare `integer i;` OUTSIDE the always block. Use `for(i=0; i<32; i=i+1)`.
203
+ - **CASTING**: Do NOT use `signed'(x)`. Use `$signed(x)` for signed arithmetic.
204
+
205
+ 4. **Output Format**:
206
+ - Return ONLY the code wrapped in ```verilog fences
207
+ - Add comments explaining the architecture.
208
+
209
+ Example format:
210
+ ```verilog
211
+ module {name} #(parameter WIDTH=32) (
212
+ input logic clk,
213
+ input logic rst_n
214
+ );
215
+ // 1. Declarations
216
+ logic [WIDTH-1:0] data_reg;
217
+ integer i;
218
+
219
+ // 2. Continuous Assignments
220
+ assign w_ready = (data_reg != 0);
221
+
222
+ // 3. Sequential Logic
223
+ always_ff @(posedge clk or negedge rst_n) begin
224
+ if (!rst_n) begin
225
+ data_reg <= 0;
226
+ // Loop example
227
+ for (i=0; i<WIDTH; i=i+1) ...
228
+ end else begin
229
+ data_reg <= ...;
230
+ end
231
+ end
232
+ endmodule
233
+ ```
234
+ ''',
235
+ expected_output='Complete SystemVerilog module code in markdown fence',
236
  agent=rtl_agent
237
  )
238
+
239
+ with console.status("[cyan]AI is designing your chip (this may take 2-5 mins)...[/cyan]"):
240
  rtl_result = Crew(agents=[rtl_agent], tasks=[rtl_task]).kickoff()
241
+
242
+ rtl_raw = str(rtl_result)
243
+ log_thinking(rtl_raw, step="RTL generation")
244
+
245
+ rtl_path = write_verilog(name, rtl_raw)
246
  console.print(f" ✓ RTL saved to: [green]{rtl_path}[/green]")
247
+
 
248
  console.print("\n[bold yellow]━━━ Step 2/5: Syntax Verification ━━━[/bold yellow]")
 
249
  success, errors = run_syntax_check(rtl_path)
250
+ tries = 0
251
+ while not success and tries < max_retries:
252
+ tries += 1
253
+ console.print(f"[bold red]✗ SYNTAX ERROR (attempt {tries}/{max_retries})[/bold red]\n{errors}")
254
+ fix_prompt = f'''Fix the following SystemVerilog code so it compiles with iverilog -g2012.
255
+
256
+ CRITICAL FIXING RULES:
257
+ 1. **Unresolved Wires**: Declare ALL signals at the top of the file using `logic [width:0] name;`. If a signal is used in `assign` or `always`, it MUST be declared.
258
+ 2. **"Constant Selects" Error**: If you see "constant selects in always process", move the slicing logic OUTSIDE the always block into an `assign` statement.
259
+ - Bad: `always_comb x = reg_file[ addr[4:0] ];`
260
+ - Good: `wire [4:0] idx = addr[4:0]; always_comb x = reg_file[idx];`
261
+ 3. **Loop Syntax**: Use `integer i;` declared outside, and `for(i=0;...)`.
262
+ 4. **Logic separation**: Use `always_ff` for registers, `assign` for simple logic.
263
+
264
+ CRITICAL REQUIREMENTS:
265
+ - Keep module name exactly "{name}"
266
+ - Use Standard SystemVerilog
267
+ - Return ONLY corrected code inside ```verilog fences
268
+
269
+ Compiler error:
270
+ {errors}
271
+
272
+ Current code:
273
+ ```verilog
274
+ {open(rtl_path,'r').read()}
275
+ ```
276
+ '''
277
+ fixed = _fix_with_llm(
278
+ agent_role='VLSI Design Engineer',
279
+ goal=f'Fix SystemVerilog syntax for {name}',
280
+ prompt=fix_prompt
281
+ )
282
+ rtl_path = write_verilog(name, fixed)
283
+ success, errors = run_syntax_check(rtl_path)
284
+
285
  if not success:
286
  console.print(f"[bold red]✗ SYNTAX ERROR:[/bold red]\n{errors}")
287
  raise typer.Exit(1)
288
  console.print(" ✓ Verilog syntax is [green]valid[/green]")
289
 
290
+ # ===== STEP 3: Generate Testbench (self-checking) =====
291
  console.print("\n[bold yellow]━━━ Step 3/5: Generating Testbench ━━━[/bold yellow]")
292
+
 
293
  with open(rtl_path, 'r') as f:
294
  rtl_code = f.read()
295
+
296
+ tb_agent = get_testbench_agent(
 
 
 
297
  llm=llm,
298
+ goal=f'Create a self-checking testbench for {name}',
299
+ verbose=show_thinking
300
  )
301
+
302
  tb_task = Task(
303
+ description=f'''Create a SystemVerilog self-checking testbench for this module:
304
+
305
  ```verilog
306
  {rtl_code}
307
  ```
308
 
309
+ CRITICAL VERIFICATION RULES (DO NOT VIOLATE):
310
+ 1. **Module Name**: Must be "{name}_tb"
311
+ 2. **Signal Directions**:
312
+ - **DUT INPUTS** must be declared as `logic` (or `reg`) in the Testbench and **DRIVEN** in `initial` blocks.
313
+ - **DUT OUTPUTS** must be declared as `logic` (or `wire`) in the Testbench and **OBSERVED** only.
314
+ - **NEVER** assign values to DUT OUTPUT signals in the Testbench (e.g. if `ready` is an output of DUT, do NOT write `ready = 1`).
315
+ 3. **Data Types**: Use `logic` for everything.
316
+ 4. **Instantiation**: Use explicit name based connection `.port(signal)`.
317
+ 5. **Clock/Reset**:
318
+ - Clock: `initial forever #5 clk = ~clk;`
319
+ - Reset: Drive `rst_n` low for 20 units, then high.
320
+ 6. **Reporting**:
321
+ - Use `$dumpfile("{name}.vcd")` and `$dumpvars(0, {name}_tb)`.
322
+ - On success: `$display("TEST PASSED");`
323
+ - On failure: `$display("TEST FAILED");`
324
+ - Always end with `$finish;`
325
+
326
+ Return ONLY the testbench code wrapped in ```verilog fences
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
327
  ''',
328
+ expected_output='Complete SystemVerilog testbench in markdown fence',
329
  agent=tb_agent
330
  )
331
+
332
  with console.status("[cyan]AI is creating testbench...[/cyan]"):
333
  tb_result = Crew(agents=[tb_agent], tasks=[tb_task]).kickoff()
334
+
335
+ tb_raw = str(tb_result)
336
+ log_thinking(tb_raw, step="Testbench generation")
337
+
338
+ tb_path = write_verilog(name, tb_raw, is_testbench=True)
339
  console.print(f" ✓ Testbench saved to: [green]{tb_path}[/green]")
340
 
341
+ # ===== STEP 4: Run Simulation with fix loop =====
342
  console.print("\n[bold yellow]━━━ Step 4/5: Running Simulation ━━━[/bold yellow]")
343
+
344
  sim_success, sim_output = run_simulation(name)
345
+ sim_tries = 0
346
+ while not sim_success and sim_tries < max_retries:
347
+ sim_tries += 1
348
+ console.print(f"[bold red]✗ SIMULATION FAILED (attempt {sim_tries}/{max_retries})[/bold red]")
349
+
350
+ sim_output_text = sim_output or ""
351
+
352
+ # 1) If compilation failed, fix TB first.
353
+ if "Compilation failed:" in sim_output_text or "syntax error" in sim_output_text:
354
+ fix_tb_prompt = f'''Fix this SystemVerilog testbench so it compiles and avoids directionality errors.
355
+
356
+ CRITICAL FIXING RULES:
357
+ 1. **Unresolved Wires**: If you see "Unable to assign to unresolved wires", it means you are driving a DUT OUTPUT. Stop driving it!
358
+ 2. **Signal Directions**:
359
+ - Check the DUT definition.
360
+ - If a port is `output` in DUT, it is a `wire` in TB (Read-Only).
361
+ - If a port is `input` in DUT, it is a `reg/logic` in TB (Write-Only).
362
+ 3. **Format**: Return ONLY corrected testbench code inside ```verilog fences.
363
+
364
+ Simulation output / errors:
365
+ {sim_output_text}
366
+
367
+ Current RTL (do not modify unless absolutely necessary):
368
+ ```verilog
369
+ {open(rtl_path,'r').read()}
370
+ ```
371
+
372
+ Current testbench:
373
+ ```verilog
374
+ {open(tb_path,'r').read()}
375
+ ```
376
+ '''
377
+ fixed_tb = _fix_with_llm(
378
+ agent_role='Verification Engineer',
379
+ goal=f'Fix testbench for {name}',
380
+ prompt=fix_tb_prompt
381
+ )
382
+ tb_path = write_verilog(name, fixed_tb, is_testbench=True)
383
+ sim_success, sim_output = run_simulation(name)
384
+ continue
385
+
386
+ # 2) If TB ran but failed logically, fix RTL to satisfy TB.
387
+ if "TEST FAILED" in sim_output_text or "TEST PASSED" not in sim_output_text:
388
+ fix_rtl_prompt = f'''The simulation did not pass. Fix the RTL (module "{name}") so that the testbench passes.
389
+
390
+ CRITICAL REQUIREMENTS:
391
+ - Keep module name exactly "{name}"
392
+ - SystemVerilog only
393
+ - Keep ports: clk, rst_n (active-low) present
394
+ - Return ONLY corrected RTL code inside ```verilog fences
395
+
396
+ Simulation output:
397
+ {sim_output_text}
398
+
399
+ Current testbench (do not change in this step):
400
+ ```verilog
401
+ {open(tb_path,'r').read()}
402
+ ```
403
+
404
+ Current RTL:
405
+ ```verilog
406
+ {open(rtl_path,'r').read()}
407
+ ```
408
+ '''
409
+ fixed_rtl = _fix_with_llm(
410
+ agent_role='VLSI Design Engineer',
411
+ goal=f'Fix RTL behavior for {name}',
412
+ prompt=fix_rtl_prompt
413
+ )
414
+ rtl_path = write_verilog(name, fixed_rtl)
415
+
416
+ success, errors = run_syntax_check(rtl_path)
417
+ if not success:
418
+ sim_output = f"RTL fix introduced syntax error:\n{errors}"
419
+ continue
420
+
421
+ sim_success, sim_output = run_simulation(name)
422
+ continue
423
+
424
+ # Default: improve TB robustness
425
+ fix_tb_prompt = f'''Improve this Verilog-2005 testbench so it robustly checks the design and prints TEST PASSED/TEST FAILED.
426
+
427
+ CRITICAL REQUIREMENTS:
428
+ - Testbench module name must be "{name}_tb"
429
+ - Must print exactly "TEST PASSED" on success
430
+ - Must print exactly "TEST FAILED" on failure
431
+ - Return ONLY corrected testbench code inside ```verilog fences
432
+
433
+ Current RTL:
434
+ ```verilog
435
+ {open(rtl_path,'r').read()}
436
+ ```
437
+
438
+ Current testbench:
439
+ ```verilog
440
+ {open(tb_path,'r').read()}
441
+ ```
442
+ '''
443
+ fixed_tb = _fix_with_llm(
444
+ agent_role='Verification Engineer',
445
+ goal=f'Improve testbench for {name}',
446
+ prompt=fix_tb_prompt
447
+ )
448
+ tb_path = write_verilog(name, fixed_tb, is_testbench=True)
449
+ sim_success, sim_output = run_simulation(name)
450
+
451
  if not sim_success:
452
  console.print(f"[bold red]✗ SIMULATION FAILED:[/bold red]\n{sim_output}")
453
  raise typer.Exit(1)
454
+
 
455
  sim_lines = sim_output.strip().split('\n')
456
+ for line in sim_lines[-8:]:
457
  console.print(f" [dim]{line}[/dim]")
458
  console.print(" ✓ Simulation [green]passed[/green]")
459
 
460
+ if skip_openlane:
461
+ console.print("\n[bold green]✓ Stopped after simulation (--skip-openlane).[/bold green]")
462
+ return
463
+
464
  # ===== STEP 5: Run OpenLane =====
465
  console.print("\n[bold yellow]━━━ Step 5/5: Running OpenLane (RTL → GDSII) ━━━[/bold yellow]")
466
  console.print(" [dim]This may take 5-10 minutes...[/dim]")
467
 
468
+ # Create/overwrite config.tcl from template (ensure RTL-only synthesis)
469
  template_config = f"{OPENLANE_ROOT}/designs/simple_counter/config.tcl"
470
  new_config = f"{OPENLANE_ROOT}/designs/{name}/config.tcl"
471
+
472
+ if os.path.exists(template_config):
473
  with open(template_config, 'r') as f:
474
  content = f.read().replace("simple_counter", name)
475
+
476
+ # Ensure we only synthesize the RTL, not the testbench.
477
+ # Handle common template patterns.
478
+ content = content.replace(
479
+ "set ::env(VERILOG_FILES) [glob $::env(DESIGN_DIR)/src/*.v]",
480
+ f"set ::env(VERILOG_FILES) \"$::env(DESIGN_DIR)/src/{name}.v\""
481
+ )
482
+ content = content.replace(
483
+ "set ::env(VERILOG_FILES) \"$::env(DESIGN_DIR)/src/simple_counter.v\"",
484
+ f"set ::env(VERILOG_FILES) \"$::env(DESIGN_DIR)/src/{name}.v\""
485
+ )
486
+
487
+ os.makedirs(os.path.dirname(new_config), exist_ok=True)
488
  with open(new_config, 'w') as f:
489
  f.write(content)
490
+ console.print(f" ✓ Config written: [green]{new_config}[/green]")
491
+ else:
492
+ console.print(" [bold red]✗ Missing OpenLane template config (designs/simple_counter/config.tcl)[/bold red]")
493
+ raise typer.Exit(1)
494
 
495
  ol_success, ol_result = run_openlane(name)
496
 
src/agentic/config.py CHANGED
@@ -1,15 +1,24 @@
1
  import os
 
2
 
3
  # Paths
4
  WORKSPACE_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
 
 
5
  OPENLANE_ROOT = os.environ.get("OPENLANE_ROOT", os.path.expanduser("~/OpenLane"))
6
  DESIGNS_DIR = os.path.join(OPENLANE_ROOT, "designs")
7
  SCRIPTS_DIR = os.path.join(WORKSPACE_ROOT, "scripts")
8
 
9
  # LLM Configuration
 
 
 
 
 
 
10
  LLM_MODEL = os.environ.get("LLM_MODEL", "ollama/deepseek-r1")
11
  LLM_BASE_URL = os.environ.get("LLM_BASE_URL", "http://localhost:11434")
12
- LLM_API_KEY = os.environ.get("OPENAI_API_KEY", "NA")
13
 
14
  # Tool Settings
15
  PDK_ROOT = os.environ.get('PDK_ROOT', os.path.expanduser('~/.ciel'))
 
1
  import os
2
+ from dotenv import load_dotenv
3
 
4
  # Paths
5
  WORKSPACE_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
6
+ load_dotenv(os.path.join(WORKSPACE_ROOT, ".env"))
7
+
8
  OPENLANE_ROOT = os.environ.get("OPENLANE_ROOT", os.path.expanduser("~/OpenLane"))
9
  DESIGNS_DIR = os.path.join(OPENLANE_ROOT, "designs")
10
  SCRIPTS_DIR = os.path.join(WORKSPACE_ROOT, "scripts")
11
 
12
  # LLM Configuration
13
+ # To use GROQ (Free Cloud):
14
+ # 1. Get Key from https://console.groq.com
15
+ # 2. export GROQ_API_KEY="gsk_..."
16
+ # 3. export LLM_MODEL="openai/llama-3.3-70b-versatile" (or similar)
17
+ # 4. export LLM_BASE_URL="https://api.groq.com/openai/v1"
18
+
19
  LLM_MODEL = os.environ.get("LLM_MODEL", "ollama/deepseek-r1")
20
  LLM_BASE_URL = os.environ.get("LLM_BASE_URL", "http://localhost:11434")
21
+ LLM_API_KEY = os.environ.get("GROQ_API_KEY", os.environ.get("OPENAI_API_KEY", "NA"))
22
 
23
  # Tool Settings
24
  PDK_ROOT = os.environ.get('PDK_ROOT', os.path.expanduser('~/.ciel'))
src/agentic/tools/vlsi_tools.py CHANGED
@@ -47,16 +47,30 @@ def write_verilog(design_name, code, is_testbench=False):
47
  import re
48
  # Remove model tokens like <|begin▁of▁sentence|>
49
  clean_code = re.sub(r'<[|\|][^>]+[|\|]>', '', clean_code)
50
- # Fix SystemVerilog to Verilog-2005
51
- clean_code = clean_code.replace('always_comb', 'always @(*)')
52
- clean_code = clean_code.replace('always_ff', 'always')
53
- clean_code = clean_code.replace('logic', 'reg')
 
 
54
  # Fix time units: #5ns -> #5, #10ps -> #10
55
  clean_code = re.sub(r'#(\d+)(ns|ps|us|ms|s)\b', r'#\1', clean_code)
56
  # Fix wildcard port connections (SystemVerilog)
57
  clean_code = re.sub(r'\(\s*\.\*\s*\)', '', clean_code)
58
  # Remove any leftover special chars
59
  clean_code = re.sub(r'[▁|]', '', clean_code)
 
 
 
 
 
 
 
 
 
 
 
 
60
 
61
  with open(path, "w") as f:
62
  f.write(clean_code)
@@ -65,7 +79,7 @@ def write_verilog(design_name, code, is_testbench=False):
65
  def run_syntax_check(file_path):
66
  """Runs iverilog syntax check."""
67
  result = subprocess.run(
68
- ["iverilog", "-t", "null", file_path],
69
  capture_output=True, text=True
70
  )
71
  return result.returncode == 0, result.stderr
@@ -79,7 +93,7 @@ def run_simulation(design_name):
79
 
80
  # Compile
81
  compile_result = subprocess.run(
82
- ["iverilog", "-o", sim_out, rtl_file, tb_file],
83
  capture_output=True, text=True
84
  )
85
  if compile_result.returncode != 0:
@@ -88,13 +102,34 @@ def run_simulation(design_name):
88
  # Run
89
  run_result = subprocess.run(
90
  ["vvp", sim_out],
91
- capture_output=True, text=True,
 
92
  timeout=30
93
  )
94
- return True, run_result.stdout
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
 
96
  def run_openlane(design_name):
97
  """Triggers the OpenLane flow via Docker."""
 
 
 
98
  os.chdir(OPENLANE_ROOT)
99
 
100
  # Direct Docker command (non-interactive)
@@ -106,7 +141,7 @@ def run_openlane(design_name):
106
  "-e", "PDK=sky130A",
107
  "-e", "PWD=/openlane",
108
  OPENLANE_IMAGE,
109
- "./flow.tcl", "-design", design_name, "-tag", "agentrun", "-overwrite"
110
  ]
111
 
112
  process = subprocess.run(
@@ -119,8 +154,14 @@ def run_openlane(design_name):
119
  # Check if GDS was created
120
  gds_path = f"{OPENLANE_ROOT}/designs/{design_name}/runs/agentrun/results/final/gds/{design_name}.gds"
121
  success = os.path.exists(gds_path)
122
-
123
- return success, gds_path if success else process.stderr
 
 
 
 
 
 
124
 
125
  def run_verification(design_name):
126
  """Runs the verify_design.sh script."""
 
47
  import re
48
  # Remove model tokens like <|begin▁of▁sentence|>
49
  clean_code = re.sub(r'<[|\|][^>]+[|\|]>', '', clean_code)
50
+
51
+ # Allow SystemVerilog constructs (removed forced downgrade)
52
+ # clean_code = clean_code.replace('always_comb', 'always @(*)')
53
+ # clean_code = clean_code.replace('always_ff', 'always')
54
+ # clean_code = clean_code.replace('logic', 'reg')
55
+
56
  # Fix time units: #5ns -> #5, #10ps -> #10
57
  clean_code = re.sub(r'#(\d+)(ns|ps|us|ms|s)\b', r'#\1', clean_code)
58
  # Fix wildcard port connections (SystemVerilog)
59
  clean_code = re.sub(r'\(\s*\.\*\s*\)', '', clean_code)
60
  # Remove any leftover special chars
61
  clean_code = re.sub(r'[▁|]', '', clean_code)
62
+
63
+ # --- AUTO-FIXES FOR COMPILER COMPATIBILITY ---
64
+ # 1. Fix signed casting: signed'(val) -> $signed(val)
65
+ clean_code = re.sub(r"signed'\s*\((.*?)\)", r"$signed(\1)", clean_code)
66
+
67
+ # 2. Fix Loop Variables: "for (int i=0..." -> "for (i=0..."
68
+ # (assuming 'integer i' is declared elsewhere or we strip the type to be safe in blocks)
69
+ # clean_code = re.sub(r'for\s*\(\s*int\s+(\w+)\s*=', r'for (\1 =', clean_code)
70
+
71
+ # 3. Remove unsupported SystemVerilog qualifiers for iverilog
72
+ clean_code = re.sub(r'\bunique\s+case\b', 'case', clean_code)
73
+ clean_code = re.sub(r'\bpriority\s+case\b', 'case', clean_code)
74
 
75
  with open(path, "w") as f:
76
  f.write(clean_code)
 
79
  def run_syntax_check(file_path):
80
  """Runs iverilog syntax check."""
81
  result = subprocess.run(
82
+ ["iverilog", "-g2012", "-t", "null", file_path],
83
  capture_output=True, text=True
84
  )
85
  return result.returncode == 0, result.stderr
 
93
 
94
  # Compile
95
  compile_result = subprocess.run(
96
+ ["iverilog", "-g2012", "-o", sim_out, rtl_file, tb_file],
97
  capture_output=True, text=True
98
  )
99
  if compile_result.returncode != 0:
 
102
  # Run
103
  run_result = subprocess.run(
104
  ["vvp", sim_out],
105
+ capture_output=True,
106
+ text=True,
107
  timeout=30
108
  )
109
+
110
+ sim_text = (run_result.stdout or "") + ("\n" + run_result.stderr if run_result.stderr else "")
111
+
112
+ # Many Verilog testbenches don't set a failing process exit code.
113
+ # AgentIC requires a clear PASS marker to treat simulation as successful.
114
+ # If the TB doesn't print TEST PASSED, we'll consider it a failure so the
115
+ # verification/fix loop can improve the TB.
116
+ if "TEST PASSED" in sim_text:
117
+ return True, sim_text
118
+
119
+ # Explicit failure marker
120
+ if "TEST FAILED" in sim_text:
121
+ return False, sim_text
122
+
123
+ # Fallback: if vvp itself failed, fail. Otherwise, still fail due to missing PASS.
124
+ if run_result.returncode != 0:
125
+ return False, sim_text
126
+ return False, sim_text
127
 
128
  def run_openlane(design_name):
129
  """Triggers the OpenLane flow via Docker."""
130
+ if not PDK_ROOT or not os.path.exists(PDK_ROOT):
131
+ return False, f"PDK_ROOT not found: {PDK_ROOT}. Set PDK_ROOT env var or install Sky130 PDKs."
132
+
133
  os.chdir(OPENLANE_ROOT)
134
 
135
  # Direct Docker command (non-interactive)
 
141
  "-e", "PDK=sky130A",
142
  "-e", "PWD=/openlane",
143
  OPENLANE_IMAGE,
144
+ "./flow.tcl", "-design", design_name, "-tag", "agentrun", "-overwrite", "-ignore_mismatches"
145
  ]
146
 
147
  process = subprocess.run(
 
154
  # Check if GDS was created
155
  gds_path = f"{OPENLANE_ROOT}/designs/{design_name}/runs/agentrun/results/final/gds/{design_name}.gds"
156
  success = os.path.exists(gds_path)
157
+
158
+ if success:
159
+ return True, gds_path
160
+
161
+ error_text = (process.stderr or "")
162
+ if process.stdout:
163
+ error_text = (process.stdout + "\n" + error_text).strip()
164
+ return False, error_text
165
 
166
  def run_verification(design_name):
167
  """Runs the verify_design.sh script."""