feat: AgentIC v2.0 - Added strict Verilog syntax rules, auto-fixers, and Groq support. Added INSTALL.md for portability.
Browse files- INSTALL.md +82 -0
- Makefile +68 -0
- README.md +82 -52
- app.py +3 -3
- debug_llm.py +29 -0
- designs/simple_counter/config.tcl +27 -0
- designs/simple_counter/src/sim +97 -0
- designs/simple_counter/src/simple_counter.v +16 -0
- designs/simple_counter/src/simple_counter_tb.v +63 -0
- main.py +4 -0
- scripts/verify_design.sh +1 -1
- src/agentic/agents/designer.py +5 -7
- src/agentic/agents/testbench_designer.py +3 -3
- src/agentic/cli.py +386 -100
- src/agentic/config.py +10 -1
- src/agentic/tools/vlsi_tools.py +52 -11
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
|
| 2 |
|
| 3 |
-
|
| 4 |
|
| 5 |
-
##
|
| 6 |
|
| 7 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
|
| 9 |
-
|
| 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 |
-
##
|
| 33 |
|
| 34 |
-
The
|
| 35 |
|
| 36 |
-
### 1.
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 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 |
-
##
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
|
| 53 |
-
###
|
|
|
|
| 54 |
```bash
|
| 55 |
-
|
| 56 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
```
|
| 58 |
|
| 59 |
-
##
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 60 |
```bash
|
| 61 |
-
|
| 62 |
-
python main.py verify vicky_adder
|
| 63 |
```
|
| 64 |
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
|
|
|
| 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
|
| 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]
|
| 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
|
| 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=
|
| 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=
|
| 9 |
-
backstory='Specialist in ECE from Lucknow University. Expert in Sky130 PDK.',
|
| 10 |
-
|
| 11 |
-
|
| 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=
|
| 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=
|
| 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 +
|
|
|
|
| 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 =
|
| 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 |
-
|
|
|
|
| 68 |
)
|
| 69 |
-
|
| 70 |
rtl_task = Task(
|
| 71 |
-
description=f'''Design
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
| 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 =
|
| 116 |
-
role='Verification Engineer',
|
| 117 |
-
goal=f'Create a testbench for {name}',
|
| 118 |
-
backstory='Expert in Verilog verification and simulation.',
|
| 119 |
llm=llm,
|
| 120 |
-
|
|
|
|
| 121 |
)
|
| 122 |
-
|
| 123 |
tb_task = Task(
|
| 124 |
-
description=f'''Create a
|
| 125 |
-
|
| 126 |
```verilog
|
| 127 |
{rtl_code}
|
| 128 |
```
|
| 129 |
|
| 130 |
-
CRITICAL
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
-
|
| 134 |
-
-
|
| 135 |
-
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
```
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 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
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
| 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[-
|
| 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)
|
| 197 |
with open(template_config, 'r') as f:
|
| 198 |
content = f.read().replace("simple_counter", name)
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 205 |
with open(new_config, 'w') as f:
|
| 206 |
f.write(content)
|
| 207 |
-
console.print(f" ✓ Config
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 51 |
-
|
| 52 |
-
clean_code = clean_code.replace('
|
| 53 |
-
clean_code = clean_code.replace('
|
|
|
|
|
|
|
| 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,
|
|
|
|
| 92 |
timeout=30
|
| 93 |
)
|
| 94 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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."""
|