VGen: Automated Verilog RTL Code Generation

repository·main·Indexed 42 days ago

https://github.com/shailja-thakur/vgen

A research project utilizing fine-tuned Large Language Models (LLMs) to generate syntactically and functionally correct Verilog RTL hardware description code. It provides models such as shailja/fine-tuned-codegen-2B-Verilog and shailja/fine-tuned-codegen-6B-Verilog, accessible via HuggingFace Transformers or an OpenAI-compatible API using a customized version of fauxpilot.

Tokens
4.3K
Snippets
15
Records
16
Agent score
52%

What's inside VGen

  1. Sample Verilog code using Fauxpilot (OpenAI API compatible)

    main

    For optimized memory and compute usage with lower latency, use the customized version of fauxpilot. This method allows you to query the fine-tuned LLMs via an OpenAI-compatible API.

    1. Clone the customized repository: git clone https://github.com/shailja-thakur/fauxpilot_changes.git.
    2. Follow the specific setup instructions in the fauxpilot_changes repository.
    3. Once the server is running, use the openai Python library to send completion requests to the local endpoint (default http://127.0.0.1:5000/v1).
    import openai
    
    openai.api_key = 'dummy'
    openai.api_base = 'http://127.0.0.1:5000/v1'
    
    result = openai.Completion.create(
        engine='codegen',
        prompt='//module half adder',
        max_tokens=100,
        temperature=0.1,
        n=3,
        top_p=1.0,
        stop=["endmodule"]
    )
    
    print(result)
  2. Sample Verilog code using HuggingFace Transformers

    main

    You can perform model inference directly using the transformers library. This method requires at least one GPU (e.g., RTX4000) to run successfully. The process involves loading a pre-trained tokenizer and a fine-tuned AutoModelForCausalLM from the HuggingFace Hub, then using the .generate() method to produce Verilog code from a prompt.

    import torch
    from transformers import AutoTokenizer, AutoModelForCausalLM
    
    # Prompt
    prompt = "//module half adder "
    
    # Load model and tokenizer
    model_name = "shailja/CodeGen_2B_Verilog"
    tokenizer = AutoTokenizer.from_pretrained("shailja/fine-tuned-codegen-2B-Verilog")
    model = AutoModelForCausalLM.from_pretrained("shailja/fine-tuned-codegen-2B-Verilog").to(device)
    
    # Sample
    input_ids = tokenizer(prompt, return_tensors="pt").input_ids.to(device)
    sample = model.generate(input_ids, max_length=128, temperature=0.5, top_p=0.9)
    
    print(tokenizer.decode(sample[0], truncate_before_pattern=[r"endmodule"]) + "endmodule")
  3. Set up Google Cloud BigQuery authentication

    main

    To use BigQuery in your Python environment, you must provide your Google Cloud credentials via the GOOGLE_APPLICATION_CREDENTIALS environment variable. This variable should point to the path of your service account JSON key file.

    Note: If you encounter an ImportError: No module named 'google', ensure you have installed the necessary libraries and loaded the BigQuery extension in your notebook environment using %load_ext google.cloud.bigquery.

    import os
    from google.cloud import bigquery
    
    # Set the path to your service account JSON key
    os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = r"path/to/your-service-account-key.json"
    
    # Initialize the BigQuery client
    bqclient = bigquery.Client()
  4. Load the VGen model and tokenizer

    main

    Load the fine-tuned CodeGen_2B_Verilog model and its corresponding tokenizer using the transformers library. It is recommended to move the model to a CUDA device if available.

    import torch
    from transformers import AutoTokenizer, AutoModelForCausalLM
    
    if torch.cuda.is_available():
        device = torch.device("cuda")
        print("Using GPU")
    else:
        device = torch.device("cpu")
        print("Using CPU")
    
    model_name = "shailja/CodeGen_2B_Verilog"
    # Note: The demo uses 'shailja/fine-tuned-codegen-2B-Verilog' for both tokenizer and model
    tokenizer = AutoTokenizer.from_pretrained("shailja/fine-tuned-codegen-2B-Verilog")
    model = AutoModelForCausalLM.from_pretrained("shailja/fine-tuned-codegen-2B-Verilog").to(device)
  5. Load model and tokenizer from HuggingFace

    main

    Use the transformers library to load the pre-trained Verilog model and its corresponding tokenizer. The model shailja/fine-tuned-codegen-6B-Verilog is specifically fine-tuned for Verilog generation tasks.

    from transformers import AutoTokenizer, AutoModelForCausalLM
    
    model_name = "shailja/CodeGen_6B_Verilog"
    
    tokenizer = AutoTokenizer.from_pretrained("shailja/fine-tuned-codegen-6B-Verilog")
    model = AutoModelForCausalLM.from_pretrained("shailja/fine-tuned-codegen-6B-Verilog").to(device)
  6. Check available device info

    main

    Before loading the model, check if a CUDA-enabled GPU is available to ensure efficient inference. If torch.cuda.is_available() returns true, set the device to cuda; otherwise, fallback to cpu.

    import torch
    
    if torch.cuda.is_available():
        device = torch.device("cuda")
    else:
        device = torch.device("cpu")
  7. Install requirements for VGen

    main

    To use the VGen models, you need to install the transformers library directly from the HuggingFace GitHub repository to ensure compatibility with the fine-tuned models.

    Ensure you have a GPU available for inference, as the models are designed for CUDA execution.

    !pip install git+https://github.com/huggingface/transformers.git
  8. Sample Verilog on a per-token basis for debugging

    main

    For debugging or granular control, you can manually perform the sampling loop. This involves:

    1. Passing input_ids through the model to get logits.
    2. Applying softmax to the logits of the last token to get probabilities.
    3. Sorting the token IDs by probability.
    4. Manually appending the chosen token to input_ids using torch.cat.
    5. Checking for a stop condition (e.g., the presence of endmodule in the decoded string).
    iteration = dict()
    with torch.no_grad():
        while True:
            iteration["Input"] = input_ids
            output = model(input_ids)
    
            # select logits of the first batch and the last token and apply softmax
            next_token_logits = output.logits[0, -1, :]
            next_token_probs = torch.softmax(next_token_logits, dim=-1)
            sorted_ids = torch.argsort(next_token_probs, dim=-1, descending=True)
    
            # store tokens with highest probability
            for choice_idx in range(n):
                token_id = sorted_ids[choice_idx]
                token_prob = next_token_probs[token_id].cpu().numpy()
                token_choice = f"{tokenizer.decode(token_id)} ({100*token_prob:.2f}%)"
                iteration[f"{choice_idx+1}"] = token_choice
    
            # Append predictions to list
            input_ids = torch.cat([input_ids, sorted_ids[None,0, None]], dim=-1)
    
            # condition checks on stop_words detected
            if 'endmodule' in tokenizer.batch_decode(input_ids)[0]: break
    
    print(tokenizer.batch_decode(input_ids)[0])
  9. Access models and datasets on HuggingFace

    main
    The fine-tuned models and the Verilog code corpus used in this research are available on the HuggingFace Hub. You can search for them using the keyword verilog or find specific models like shailja/fine-tuned-codegen-2B-Verilog.
  10. Sample Verilog code using a prompt

    main

    Generate Verilog code by providing a text prompt (e.g., a comment describing a module). Use model.generate() with parameters like max_length, temperature, and top_p to control the output. You can use truncate_before_pattern to clean up the output based on specific Verilog keywords like endmodule.

    prompt = "//a half adder module "
    n_steps = 1
    n = 5
    end_pattern='endmodule'
    
    input_ids = tokenizer(prompt, return_tensors="pt").input_ids.to(device)
    
    sample = model.generate(input_ids, max_length=128, temperature=0.5, top_p=0.9)
    print(tokenizer.decode(sample[0], truncate_before_pattern=[r"endmodule"]) + end_pattern)
  11. Fetch Verilog questions and answers from StackOverflow

    main

    To collect Verilog-related discussions, you can join the posts_questions and posts_answers tables from the bigquery-public-data.stackoverflow dataset, filtering by tags or body content containing 'Verilog'.

    SELECT 
        q.tags question_tag, q.id question_id, q.title question_title, q.body question_body, 
        a.id answer_id, a.body 
    FROM `bigquery-public-data.stackoverflow.posts_questions` as q
    LEFT JOIN `bigquery-public-data.stackoverflow.posts_answers` as a
    ON a.parent_id = q.id
    WHERE regexp_contains(q.tags, 'Verilog');
  12. Sample Verilog code per token with probability distribution

    main

    For granular control or debugging, you can perform manual autoregressive sampling. This involves passing the current input_ids to the model, extracting the logits for the last token, applying softmax to get probabilities, and selecting the top-N most likely tokens. This loop continues until a stop word (like endmodule) is detected in the decoded sequence.

    iteration = dict()
    with torch.no_grad():
        while True:
            iteration["Input"] = input_ids
            output = model(input_ids)
    
            # Get logits of the last token and apply softmax
            next_token_logits = output.logits[0, -1, :]
            next_token_probs = torch.softmax(next_token_logits, dim=-1)
            sorted_ids = torch.argsort(next_token_probs, dim=-1, descending=True)
    
            # Store top n choices
            n = 5
            for choice_idx in range(n):
                token_id = sorted_ids[choice_idx]
                token_prob = next_token_probs[token_id].cpu().numpy()
                token_choice = f"{tokenizer.decode(token_id)} ({100*token_prob:.2f}%)"
                iteration[f"{choice_idx+1}"] = token_choice
    
            # Append the most likely token to input_ids for the next step
            input_ids = torch.cat([input_ids, sorted_ids[None, 0, None]], dim=-1)
    
            # Stop if 'endmodule' is detected
            if 'endmodule' in tokenizer.batch_decode(input_ids)[0]: 
                break
    
    print(tokenizer.batch_decode(input_ids)[0])