Iterative Refinement Agent for Verilog Code Generation using Small Language Models
This is a modularized, production-ready implementation of an SLM agent that generates Verilog/SystemVerilog code through iterative refinement with comprehensive validation.
✅ Modular Architecture: 15+ modules organized in 7 packages
✅ I/O Port Usage Validation: NEW feature that ensures all ports are used
✅ SLM-Optimized Prompts: Keywords and templates optimized for small language models
✅ Context Optimization: Smart prioritization and token budget management
✅ Error Categorization: Structured feedback for faster convergence
✅ Extensible Design: Easy to add new SLM backends, validators, or test runners
slm_agent/
├── __init__.py
├── main.py # Entry point
├── README.md # This file
│
├── config/ # Configuration management
│ ├── __init__.py
│ └── settings.py # AgentConfig with validation
│
├── core/ # Core orchestration
│ ├── __init__.py
│ ├── agent.py # Main coordinator
│ └── refinement_loop.py # Iteration management + port validation
│
├── llm/ # Language model interface
│ ├── __init__.py
│ ├── api_client.py # SLM API client
│ └── response_parser.py # Code extraction & validation
│
├── prompts/ # Prompt engineering
│ ├── __init__.py
│ ├── templates.py # SLM-optimized templates with keywords
│ └── prompt_builder.py # Dynamic prompt assembly
│
├── hdl/ # HDL-specific operations
│ ├── __init__.py
│ ├── code_manager.py # File I/O & context gathering
│ └── port_analyzer.py # ⭐ NEW: I/O port usage validation
│
├── testing/ # Test execution
│ ├── __init__.py
│ ├── test_runner.py # Test orchestration
│ ├── cocotb_runner.py # CocoTB tests
│ └── lint_runner.py # Verilator/Icarus linting
│
└── utils/ # Utilities
├── __init__.py
└── logger.py # Dual-output logging
from slm_agent.main import main
# Run with default configuration
exit_code = main()from slm_agent.core.agent import IterativeRefinementAgent
from slm_agent.config.settings import AgentConfig
# Custom configuration
config = AgentConfig(
max_iterations=5,
slm_model="deepseek",
enable_port_validation=True,
use_few_shot_examples=True
)
# Create and run agent
agent = IterativeRefinementAgent(config)
exit_code = agent.run()Configuration via environment variables or AgentConfig:
@dataclass
class AgentConfig:
# Iteration settings
max_iterations: int = 3
early_exit_on_success: bool = True
# SLM API settings
slm_api_url: str = "http://host.docker.internal:8000"
slm_model: str = "deepseek"
slm_max_length: int = 8192
slm_timeout: int = 300
# Prompt engineering
use_few_shot_examples: bool = True
max_context_files: int = 10
enable_port_validation: bool = True # NEW
# Testing
test_timeout: int = 120
lint_timeout: int = 30Environment variables:
SLM_API_URL: SLM API endpointSLM_MODEL: Model identifierSLM_MAX_LENGTH: Maximum generation lengthSLM_TIMEOUT: Request timeout
Validates that all module ports are actually used in the implementation:
from slm_agent.hdl.port_analyzer import PortAnalyzer
analyzer = PortAnalyzer()
result = analyzer.analyze(verilog_code)
# Result structure:
# {
# "all_ports_used": bool,
# "unused_inputs": List[str],
# "unused_outputs": List[str],
# "port_usage": Dict[str, str],
# "feedback": str
# }How it works:
- Parses module interface to extract input/output ports
- Scans module body for port references
- For inputs: Checks if port appears in expressions
- For outputs: Checks if port is assigned (<=, =, or assign)
- Generates detailed feedback for refinement
SLM-Optimized Keywords:
TASK,REQUIREMENTS,SPECIFICATIONSGENERATE,IMPLEMENT,FIX,CORRECTCONSTRAINTS,RULES,MUSTERROR,WARNING,ISSUE
Few-Shot Examples:
- Automatically selected based on task keywords
- Includes: counter, FIFO, FSM patterns
- Demonstrates best practices
1. Generate initial code (with few-shot examples)
2. Write to target file
3. Run compilation tests
4. If pass → Check port usage (NEW)
5. If ports incomplete → Refine with port feedback
6. If all good → Success!
7. If fail → Refine with error feedback
8. Repeat up to max_iterations
Automatically categorizes errors for targeted refinement:
syntax: Parse errors, unexpected tokensundeclared: Undefined variables/signalstype: Type mismatcheswidth: Bit width issueslatch: Latch inference warningstiming: Timing violations
ROLE: Expert Verilog/SystemVerilog RTL Designer
TASK: Generate Verilog/SystemVerilog RTL code
REQUIREMENTS:
{task_description}
CONTEXT FILES:
{context_files}
DESIGN PATTERNS (Examples):
{few_shot_examples}
OUTPUT FORMAT:
- Start with: module <name>
- Declare all ports with proper types
- Include proper reset logic
- Use non-blocking (<=) for sequential logic
- Use blocking (=) for combinational logic
- End with: endmodule
GENERATE: Complete synthesizable RTL code below
TASK: Complete port usage in Verilog module
CURRENT CODE (compiles but incomplete):
```verilog
{current_code}
PORT USAGE ANALYSIS:
- UNUSED INPUT PORTS: {unused_inputs}
- UNUSED OUTPUT PORTS: {unused_outputs}
REQUIREMENTS:
- MUST use all input ports in internal logic
- MUST assign all output ports
- PRESERVE existing correct functionality
- ADD necessary logic for unused ports
GENERATE: Complete RTL code with all ports properly used
## Testing
The agent supports multiple testing backends:
1. **CocoTB Tests** (preferred)
- pytest-based verification
- Comprehensive functional testing
2. **Verilator** (fallback)
- Fast lint checking
- Syntax validation
3. **Icarus Verilog** (fallback)
- Open-source simulator
- Basic syntax checks
## Extending the Agent
### Adding a New SLM Backend
```python
# In llm/api_client.py
class CustomSLMClient(SLMAPIClient):
def generate(self, prompt: str, temperature: float = 0.7):
# Custom API implementation
pass
# In hdl/
class CustomValidator:
def validate(self, code: str) -> Tuple[bool, str]:
# Custom validation logic
return success, errors# In testing/
class CustomTestRunner:
def run(self) -> Tuple[bool, str]:
# Custom test execution
return success, errors| Aspect | Monolithic | Modular |
|---|---|---|
| Maintainability | 600+ line single file | 15+ focused modules |
| Testability | Hard to unit test | Each module testable |
| Extensibility | Requires major refactoring | Add new modules easily |
| Reusability | Tightly coupled | Loosely coupled components |
| Debugging | Complex stack traces | Clear component boundaries |
| Configuration | Hard-coded values | Centralized config |
| Validation | Compilation only | Compilation + semantics |
- Context Prioritization: docs > rtl > verif
- Token Budget Management: 40% task, 40% context, 20% examples
- File Truncation: Large files truncated to 2000 chars
- Early Exit: Stop on first success
- Temperature Strategies: Lower for interfaces, higher for logic
- Multi-stage generation (interface → logic → assertions)
- Temperature strategies per generation stage
- Self-consistency with voting
- RAG with vector database for design patterns
- Constrained decoding at token level
- Automated test generation
- IEEE 1800-2017 SystemVerilog Standard
- SLM Prompt Engineering Best Practices
- ASIC Design Flow Documentation
- CocoTB Testing Framework
SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: Apache-2.0