An LLM-Driven Framework for Automated Hardware Design Space Exploration and Abstraction
The Verilog KG Builder is a Python-based tool designed to bridge the gap between raw Hardware Description Language (Verilog) and high-level architectural analysis. Unlike traditional AST parsers that focus on syntax, this tool leverages Large Language Models (LLMs) to extract semantic meaning, organizing hardware designs into a structured, hierarchical Knowledge Graph.
This graph serves as a dynamic blueprint for:
- Design Space Exploration (DSE): defining tunable parameters and constraints.
- Architectural Abstraction: automatically grouping similar implementations (Meta-Architectures).
- Code Generation: driving high-level synthesis frameworks like PyTV.
The graph strictly separates intent from implementation:
- FunctionNode (π¦): Represents what a module does (e.g., "Matrix Multiplication").
- ArchitectureNode (βοΈ): Represents how it is implemented (e.g., "Systolic Array", "Dot Product Pipeline").
The system supports MetaArchNodes (π ), which act as containers for related architectures.
- Example: Grouping
Systolic_Weight_StationaryandSystolic_Output_Stationaryunder a parent nodeSystolic_Variants. - This allows for recursive abstraction and cleaner visualization of complex design spaces.
Parameters are not just static values. The PyParameter structure supports References:
- Standalone: A fixed integer or string.
- Reference: A pointer to a parent or global configuration (e.g.,
width -> parent.system_width). - This enables "Write Once, Update Everywhere" logic for optimization scripts.
The logic is built upon Python dataclasses that support recursive serialization:
@dataclass
class FunctionNode:
name: str
architectures: List[Union[ArchitectureNode, MetaArchNode]]
@dataclass
class MetaArchNode:
name: str
children: List[Union[ArchitectureNode, MetaArchNode]]
# Aggregates semantic groups of architectures
@dataclass
class ArchitectureNode:
name: str
parameters: List[PyParameter] # Supports reference linking
sub_modules: List[SubModuleSlot]The builder operates via an "Action" interface. The LLM analyzes code or graph state and returns JSON actions:
AddFunctionNodeAddArchitectureNodeAddMetaArchNode(Refinement)UpdateParameterReference
# Requires Python 3.8+
pip install openai pyvisThe core entry point is call_llm_to_update_kg. It operates in two distinct modes based on the input.
Parses new Verilog files and expands the graph.
from verilog_kg import VerilogKGBuilder
kg = VerilogKGBuilder()
# Parse a Verilog file to add Functions/Architectures
kg.call_llm_to_update_kg(
api_key="sk-...",
base_url="https://api.openai.com/v1",
model="gpt-4o",
prompt_path="system_prompt.md",
verilog_path="src/matrix_mul.v" # <--- Path provided
)Analyzes the existing graph to group related nodes into Meta-Architectures without reading new code.
# Pass verilog_path=None to trigger Refinement Mode
kg.call_llm_to_update_kg(
api_key="sk-...",
base_url="...",
model="gpt-4o",
verilog_path=None # <--- Triggers Meta-Arch analysis
)# Print ASCII Tree to Console
kg.print_ascii_tree()
# Save interactive HTML visualization
kg.visualize_graph("design_space.html")
# Save/Load Checkpoints
kg.save_ckpt("kg_v1.json")
kg.load_ckpt("kg_v1.json")The tool generates interactive HTML graphs using PyVis:
- π¦ Blue Squares: Function Definitions.
- π’ Green Dots: Concrete Architectures.
- π£ Purple Diamonds: Meta-Architectures (Aggregators).
- π΄ Red Arrows: Sub-module instantiations.
PyGAST is a versatile framework for constructing and representing hardware design logic as a Generic Abstract Syntax Tree (AST). It uses a structured list of "Actions" to build and describe hardware elements like modules, parameters, assignments, branches, and loops.
This document outlines the core concepts of PyGAST, its supported node types, and how to construct and use it.
GastNode: The base data structure for all nodes in the tree. Each node contains fundamental information such asid,node_type, andparent_id.Action: A JSON dictionary (or Pythondict) that describes an operation to create or modify aGastNode. For example,{"action": "DefineModule", "id": "root", ...}represents the definition of a module node.PyGASTBuilder: The main utility class responsible for processing a list ofActions, building the in-memory tree structure, managing multiple trees (a forest), and providing features like visualization, saving, and loading.
PyGAST represents different hardware design constructs through specific node types. Each node type is created by a corresponding action string.
| Node Type | action String |
Description |
|---|---|---|
ModuleDefNode |
DefineModule |
Represents the definition of a hardware module, including its name and arguments (ports). This is typically the root of a design tree. |
ParamDefNode |
DefineModuleParam |
Defines a parameter within a module, such as a data width or matrix dimension, along with its default value. |
AssignNode |
Assign |
Represents an assignment statement, where a variable (e.g., a wire or reg) is assigned a value or expression. |
BranchNode |
If |
Represents a conditional (if-else) block. It contains a condition and lists of child nodes for both the true and false branches. |
LoopNode |
ForLoop |
Represents a for loop, defined by an iterator and an iterable range. The body of the loop consists of its child nodes. |
InstanceNode |
Instantiate |
Represents the instantiation of another module. It includes the name of the target module and mappings for its parameters. |
There are two primary ways to construct a PyGAST.
This is the fundamental method. You define a Python list of Action dictionaries and then pass it to the PyGASTBuilder.
Steps:
-
Define the
actionslist: Each action is a dictionary containing theactiontype, a uniqueid, aparent_id, and other relevant keys.# Example: Partial actions for a Matrix Multiplier module actions = [ { "action": "DefineModule", "id": "root_matmul", "name": "MatrixMult", "args": ["config", "clk"], "desc": "Main Matrix Multiplier Module" }, { "action": "DefineModuleParam", "id": "node_param_N", "parent_id": "root_matmul", # Belongs to which module "name": "N", "value": "16", "desc": "Matrix dimension N" }, { "action": "Assign", "id": "assign_full_signal", "parent_id": "root_matmul", "var": "full", "expr": "1", "desc": "Set full signal" } ]
-
Use
PyGASTBuilderto build the tree:from ast_generation import PyGASTBuilder # Initialize the builder builder = PyGASTBuilder() # Process the actions list to build the AST builder.process_actions(actions) print("PyGAST has been successfully built.")
The PyGASTBuilder can interface with a Large Language Model (LLM) to automatically convert a user's natural language requirements into an Action list and build the AST.
Steps:
-
Prepare configuration: This includes your API Key, model name, prompt file path, and user requirement.
API_KEY = "YOUR_API_KEY" BASE_URL = "YOUR_API_ENDPOINT" MODEL = "your-model-name" prompt_path = "prompt_ast_construction.md" user_requirement = "Create a highly reconfigurable matrix multiplier."
-
Call the
build_from_llm_with_debatemethod: This method initiates a "debate" loop where generator and critic models work together to iteratively refine the AST.from ast_generation import PyGASTBuilder builder = PyGASTBuilder() # Build from natural language requirement builder.build_from_llm_with_debate( api_key=API_KEY, base_url=BASE_URL, model=MODEL, prompt_path=prompt_path, user_requirement=user_requirement, # ... other log and config paths ) print("PyGAST has been automatically built from the user requirement.")
Once built, you can visualize, save, and load the AST.
Two visualization options are available: ASCII art and an interactive HTML file.
-
ASCII Visualization: Prints the tree structure directly to the console.
ascii_art = builder.visualize_ascii() print(ascii_art)
-
HTML Visualization: Generates an interactive HTML file where nodes are colored by type, making it easy to inspect the tree.
Note: This feature requires the
pyvislibrary (pip install pyvis).builder.visualize_html(filename="pygast_tree.html") print("HTML visualization saved to pygast_tree.html")
You can save the current in-memory state of the AST to a JSON file and restore it later.
-
Save Checkpoint:
ckpt_file = "my_design.json" builder.save_ckpt(ckpt_file) print(f"Checkpoint saved to {ckpt_file}")
-
Load Checkpoint:
# Simulate a program restart with a new, empty builder new_builder = PyGASTBuilder() # Load from the file new_builder.load_ckpt(ckpt_file) print(f"Checkpoint loaded from {ckpt_file}") # Verify the loaded content print(new_builder.visualize_ascii())
This example demonstrates the full manual workflow from construction to saving and loading.
from ast_generation import PyGASTBuilder
import os
# 1. Define an Action list for multiple trees (a forest)
multi_tree_actions = [
# Tree 1: MatrixMult
{"action": "DefineModule", "id": "root_matmul", "name": "MatrixMult"},
{"action": "DefineModuleParam", "id": "p1", "parent_id": "root_matmul", "name": "N", "value": "16"},
# Tree 2: FIFOBuffer
{"action": "DefineModule", "id": "root_fifo", "name": "FIFOBuffer"},
{"action": "DefineModuleParam", "id": "p2", "parent_id": "root_fifo", "name": "DEPTH", "value": "32"},
]
# 2. Initialize builder and process Actions
builder = PyGASTBuilder()
builder.process_actions(multi_tree_actions)
print("--- AST built from actions ---")
# 3. Visualize the trees
print("\n--- Visualizing Trees (ASCII) ---")
ascii_art = builder.visualize_ascii()
print(ascii_art)
# 4. Save a Checkpoint
ckpt_file = "test_forest.json"
builder.save_ckpt(ckpt_file)
print(f"\n--- Checkpoint saved to {ckpt_file} ---")
# 5. Simulate a restart: create a new builder and load the checkpoint
new_builder = PyGASTBuilder()
new_builder.load_ckpt(ckpt_file)
print("\n--- Loaded Checkpoint into new builder ---")
# 6. Verify the loaded structure
print("\n--- Visualizing Loaded Trees ---")
loaded_ascii = new_builder.visualize_ascii()
print(loaded_ascii)
# 7. Clean up
os.remove(ckpt_file)Contributions are welcome! Areas for improvement include:
- Enhancing the System Prompt for complex parameter extraction.
- Adding support for SystemVerilog interfaces.
MIT License

