View on GitHub

Parser Module API

The parsers module handles conversion of Verilog netlists and text formats to structured JSON data.

Module: verilog_parser.py

Parses gate-level Verilog netlists from synthesis tools (Cadence Genus, Synopsys Design Compiler).

Functions

parse(input_filename, output_filename)

Parse a Verilog netlist and convert to internal format.

Parameters:

Returns:

Raises:

Output Files: Creates both text and JSON formats:

  1. <output_filename> - Text format with comments
  2. <output_filename>.json - JSON format with structured data

Example:

from opentestability.parsers.verilog_parser import parse

# Parse and create both .txt and .json
output_path = parse("serial_ALU.v", "serial_alu.txt")
# Creates:
#   data/parsed/serial_alu.txt
#   data/parsed/serial_alu.json

CLI Usage:

python3 -m opentestability.parsers.verilog_parser circuit.v

parse_verilog_netlist(file_path)

Parse Verilog netlist using pyverilog library.

Parameters:

Returns:

Structure:

{
  "module_name": {
    "pi": ["input1", "input2", "bus[7:0]"],
    "po": ["output1", "result[3:0]"],
    "wires": ["n1", "n2", "n3"],
    "instances": [
      ("AND2X1", "gate1", {"A": "input1", "B": "input2", "Y": "n1"}),
      ("INVX1", "gate2", {"A": "n1", "Y": "output1"})
    ]
  }
}

Example:

from opentestability.parsers.verilog_parser import parse_verilog_netlist

modules = parse_verilog_netlist("path/to/circuit.v")

for module_name, module_data in modules.items():
    print(f"Module: {module_name}")
    print(f"  Inputs: {module_data['pi']}")
    print(f"  Outputs: {module_data['po']}")
    print(f"  Gates: {len(module_data['instances'])}")

format_port(port)

Format port with width information if available.

Parameters:

Returns:

Example:

# Internal usage during parsing
formatted = format_port(port_node)
# Returns: "data[7:0]" for buses, "clk" for single bits

get_argname_name(arg)

Extract argument name from AST node.

Parameters:

Returns:

Example:

# Internal usage during parsing
arg_name = get_argname_name(arg_node)
# Returns: "signal_name" or "bus[3]"

Supported Verilog Constructs

Module Definitions

module my_circuit(
  input clk, reset,
  input [3:0] data_in,
  output [7:0] result,
  output valid
);

Port Declarations

Standard Cell Instances

AND2X1 gate1(.A(in1), .B(in2), .Y(out1));
NAND3X1 gate2(.A(a), .B(b), .C(c), .Y(result));
DFFRX1 reg1(.D(d), .CK(clk), .Q(q), .QN(qn));

Wire Declarations

wire n1, n2, n3;
wire [7:0] bus_signal;

Output Port Detection

The parser recognizes standard output port names:

Configuration:

OUTPUT_PORT_NAMES = {'Z', 'ZN', 'Q', 'QN', 'Y', 'S', 'CO'}

Module: json_converter.py

Converts text-based parsed netlists to JSON format.

Functions

convert_txt_to_json(input_filename)

Convert a parsed text netlist to JSON format.

Parameters:

Returns:

Raises:

Example:

from opentestability.parsers.json_converter import convert_txt_to_json

json_path = convert_txt_to_json("circuit.txt")
# Creates: data/parsed/circuit.json

CLI Usage:

python3 -m opentestability.parsers.json_converter circuit.txt

parse_netlist_txt(txt_path)

Parse a text-based netlist file and extract components.

Parameters:

Returns:

Structure:

{
  "primary_inputs": ["clk", "reset", "A[3:0]", "B[3:0]"],
  "primary_outputs": ["result[3:0]", "zero", "carry"],
  "gates": [
    {
      "type": "AND2X1",
      "output": "n1",
      "inputs": ["a", "b"]
    },
    {
      "type": "INVX1",
      "output": "z",
      "inputs": ["n1"]
    }
  ]
}

Example:

from opentestability.parsers.json_converter import parse_netlist_txt
from pathlib import Path

data = parse_netlist_txt(Path("data/parsed/circuit.txt"))
print(f"Found {len(data['gates'])} gates")

Text Format Specification

The text format uses a simple, human-readable structure:

# Primary Inputs
clk reset A[3:0] B[3:0] opcode[1:0]

# Primary Outputs
result[3:0] zero carry

# Complete Paths
AND2X1 out(n1) in(a b)
OR2X1 out(n2) in(n1 c)
INVX1 out(z) in(n2)

INPUT clk reset A[3:0] B[3:0] opcode[1:0]
OUTPUT result[3:0] zero carry

Format Rules:

  1. Lines starting with # are comments (ignored)
  2. Empty lines are ignored
  3. INPUT lines define primary inputs (space-separated)
  4. OUTPUT lines define primary outputs (space-separated)
  5. Gate lines follow format: <TYPE> out(<output>) in(<input1> <input2> ...)

Integration Examples

Complete Parsing Workflow

from opentestability.parsers.verilog_parser import parse
from opentestability.parsers.json_converter import convert_txt_to_json
import json

# Method 1: Direct parse (creates both formats automatically)
parse("circuit.v", "circuit.txt")

# Method 2: Parse to text, then convert to JSON
# (if you have old text files without JSON)
json_path = convert_txt_to_json("old_circuit.txt")

# Load and use the JSON data
with open("data/parsed/circuit.json", 'r') as f:
    netlist = json.load(f)
    
print(f"Circuit has {len(netlist['gates'])} gates")
print(f"Inputs: {netlist['primary_inputs']}")
print(f"Outputs: {netlist['primary_outputs']}")

Custom Parsing

from opentestability.parsers.verilog_parser import parse_verilog_netlist

# Parse with full control
modules = parse_verilog_netlist("/custom/path/circuit.v")

# Process specific module
module_name = "serial_alu"
if module_name in modules:
    mod_data = modules[module_name]
    
    # Extract gate types
    gate_types = set(inst[0] for inst in mod_data['instances'])
    print(f"Gate types used: {gate_types}")
    
    # Count flip-flops
    ff_count = sum(1 for inst in mod_data['instances'] 
                   if 'DFF' in inst[0])
    print(f"Flip-flops: {ff_count}")

Batch Processing

from pathlib import Path
from opentestability.parsers.verilog_parser import parse

input_dir = Path("data/input")
verilog_files = input_dir.glob("*.v")

for vfile in verilog_files:
    output_name = f"{vfile.stem}.txt"
    try:
        parse(vfile.name, output_name)
        print(f"✓ Parsed {vfile.name}")
    except Exception as e:
        print(f"✗ Error parsing {vfile.name}: {e}")

Error Handling

Common Errors

FileNotFoundError:

try:
    parse("nonexistent.v", "output.txt")
except FileNotFoundError as e:
    print(f"File not found: {e}")

Syntax Errors in Verilog:

# pyverilog will raise ParseError for invalid Verilog
from pyverilog.vparser.parser import ParseError

try:
    modules = parse_verilog_netlist("bad_syntax.v")
except ParseError as e:
    print(f"Verilog syntax error: {e}")

Invalid Text Format:

try:
    data = parse_netlist_txt(Path("malformed.txt"))
except Exception as e:
    print(f"Parse error: {e}")

Performance Considerations

Large Netlists

For circuits with 10,000+ gates:

Optimization Tips

  1. Use JSON format for repeated access (faster than reparsing Verilog)
  2. Parse once, analyze multiple times
  3. Consider batching for multiple files
# Good: Parse once, analyze many times
parse("large_circuit.v", "large_circuit.txt")
# Now use large_circuit.json for all subsequent analysis

# Not optimal: Reparsing for each analysis
# parse() → analyze() → parse() → analyze() ...

Dependencies

Required Packages

System Requirements


Future Enhancements

Planned features: