Where in RTL does it get assigned?

From Verific Design Automation FAQ
Revision as of 21:42, 30 March 2021 by Hoa (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

This example illustrates how to find where a signal gets assigned in the RTL code.

C++:

#include <iostream>

#include "veri_file.h"
#include "VeriModule.h"
#include "VeriId.h"
#include "VeriMisc.h"
#include "VeriExpression.h"
#include "veri_tokens.h"
#include "VeriVisitor.h"
#include "VeriStatement.h"

#ifdef VERIFIC_NAMESPACE
using namespace Verific ;
#endif

class MyVisitor : public VeriVisitor
{
public:
    MyVisitor() : VeriVisitor(), _assign(0) { }
    virtual ~MyVisitor() { _assign = 0 ; }

    virtual void VERI_VISIT(VeriIdRef, node) ;
    virtual void VERI_VISIT(VeriIndexedId, node) ;
    virtual void VERI_VISIT(VeriIndexedMemoryId, node) ;
    virtual void VERI_VISIT(VeriSelectedName, node) ;

    virtual void VERI_VISIT(VeriNetRegAssign, node) ;
    virtual void VERI_VISIT(VeriBlockingAssign, node) ;
    virtual void VERI_VISIT(VeriNonBlockingAssign, node) ;
    virtual void VERI_VISIT(VeriBinaryOperator, node) ;

private:
    VeriTreeNode *_assign ;
} ; // class MyVisitor

void
MyVisitor::VERI_VISIT(VeriIdRef, node)
{
    VeriIdDef *id = node.GetId() ;
    if (!_assign || !id) return ;

    char *str = _assign->GetPrettyPrintedString() ;
    unsigned l = Strings::len(str) ;
    if (str && (str[l-1] == '\n')) str[l-1] = '\0' ;
    node.Info("'%s' is assigned here as: %s", id->Name(), str) ;
    Strings::free(str) ;
}

void
MyVisitor::VERI_VISIT(VeriSelectedName, node)
{
    VeriIdDef *id = node.GetId() ;
    if (!_assign || !id) return ;

    char *str = _assign->GetPrettyPrintedString() ;
    unsigned l = Strings::len(str) ;
    if (str && (str[l-1] == '\n')) str[l-1] = '\0' ;
    char *my_name = node.GetPrettyPrintedString() ;
    node.Info("'%s' is assigned here as: %s", my_name, str) ;
    Strings::free(my_name) ;
    Strings::free(str) ;
}

void
MyVisitor::VERI_VISIT(VeriIndexedId, node)
{
    // Call of Base class Visit
    VeriVisitor::VERI_VISIT_NODE(VeriName, node) ;

    TraverseNode(node.GetPrefix()) ;

    // Do not traverse the index!
}

void
MyVisitor::VERI_VISIT(VeriIndexedMemoryId, node)
{
    // Call of Base class Visit
    VeriVisitor::VERI_VISIT_NODE(VeriName, node) ;

    TraverseNode(node.GetPrefix()) ;

    // Do not traverse the index!
}

void
MyVisitor::VERI_VISIT(VeriNetRegAssign, node)
{
    // Call of Base class Visit
    VeriVisitor::VERI_VISIT_NODE(VeriTreeNode, node) ;

    VERIFIC_ASSERT(!_assign) ; // It should not be set here
    _assign = &node ;

    // Traverse the left hand side expression
    TraverseNode(node.GetLValExpr()) ;

    _assign = 0 ;

    // Trverse the right hand side expression
    TraverseNode(node.GetRValExpr()) ;
}

void
MyVisitor::VERI_VISIT(VeriBlockingAssign, node)
{
    // Call of Base class Visit
    VeriVisitor::VERI_VISIT_NODE(VeriStatement, node) ;

    VERIFIC_ASSERT(!_assign) ; // It should not be set here
    _assign = &node ;

    // Traverse left hand side of assignment
    TraverseNode(node.GetLVal()) ;

    // Pre increment decrement has right expression set, so do not reset it here:
    if ((node.OperType() != VERI_INC_OP) && (node.OperType() != VERI_DEC_OP)) _assign = 0 ;

    // Traverse the value of assignment
    TraverseNode(node.GetValue()) ;

    _assign = 0 ;

    // Traverse delay or event control
    TraverseNode(node.GetControl()) ;
}

void
MyVisitor::VERI_VISIT(VeriNonBlockingAssign, node)
{
    // Call of Base class Visit
    VeriVisitor::VERI_VISIT_NODE(VeriStatement, node) ;

    VERIFIC_ASSERT(!_assign) ; // It should not be set here
    _assign = &node ;

    // Traverse left hand side of assignment
    TraverseNode(node.GetLVal()) ;

    _assign = 0 ;

    // Traverse delay or event control
    TraverseNode(node.GetControl()) ;

    // Traverse the value
    TraverseNode(node.GetValue()) ;
}

void
MyVisitor::VERI_VISIT(VeriBinaryOperator, node)
{
    switch (node.OperType()) {
        case VERI_INC_OP :
        case VERI_DEC_OP :
        case VERI_PLUS_ASSIGN :
        case VERI_MIN_ASSIGN :
        case VERI_MUL_ASSIGN :
        case VERI_DIV_ASSIGN :
        case VERI_MOD_ASSIGN :
        case VERI_AND_ASSIGN :
        case VERI_OR_ASSIGN :
        case VERI_XOR_ASSIGN :
        case VERI_LSHIFT_ASSIGN :
        case VERI_RSHIFT_ASSIGN :
        case VERI_ALSHIFT_ASSIGN :
        case VERI_ARSHIFT_ASSIGN :
        case VERI_EQUAL_ASSIGN : break ;
        default:
        {
            // Not interested in this binary operator:
            VeriVisitor::VERI_VISIT_NODE(VeriBinaryOperator, node) ;
            return ;
        }
    }

    // Call of Base class Visit
    VeriVisitor::VERI_VISIT_NODE(VeriExpression, node) ;

    VERIFIC_ASSERT(!_assign) ; // It should not be set here
    _assign = &node ;

    // Traverse left operand
    TraverseNode(node.GetLeft()) ;

    // Pre increment decrement has right expression set, so do not reset it here:
    if ((node.OperType() != VERI_INC_OP) && (node.OperType() != VERI_DEC_OP)) _assign = 0 ;

    // Traverse right operand
    TraverseNode(node.GetRight()) ;

    _assign = 0 ;
}

int main(void)
{
    if (!veri_file::Analyze("test.v", veri_file::SYSTEM_VERILOG, "work", veri_file::SFCU)) return 1 ;

    VeriModule *mod = veri_file::GetModule("prep5") ;
    if (!mod) return 2 ;

    MyVisitor mv ;
    mod->Accept(mv) ;

    return 0 ;
}
 

Python script:

#!/usr/bin/python

import sys
sys.path.append('../py')
import Verific

class MyVisitor (Verific.VeriPythonVisitor) :

    assign = 0

    def __init__ (self) :
        Verific.VeriPythonVisitor.__init__ (self, 0)
        self.assign = 0

    def VisitVeriIdRef (self, node) :
        id = node.GetId()
        if (not id or not self.assign) :
            return

        str = self.assign.GetPrettyPrintedString()
        str = str.replace('\n', '')
        node.Info("'" + id.Name() + "' is assigned here as: " + str)

    def VisitVeriSelectedName (self, node) :
        id = node.GetId()
        if (not id or not self.assign) :
            return

        str = self.assign.GetPrettyPrintedString()
        str = str.replace('\n', '')
        my_name = node.GetPrettyPrintedString()
        node.Info("'" + my_name + "' is assigned here as: " + str)

    def VisitVeriIndexedId (self, node) :
        # Call of Base class Visit
        Verific.VeriPythonVisitor.VisitVeriName(self, node)

        self.TraverseNode(node.GetPrefix())

        # Do not traverse the index!

    def VisitVeriIndexedMemoryId (self, node) :
        # Call of Base class Visit
        Verific.VeriPythonVisitor.VisitVeriName(self, node)

        self.TraverseNode(node.GetPrefix())

        # Do not traverse the index!

    def VisitVeriNetRegAssign (self, node) :
        # Call of Base class Visit
        Verific.VeriPythonVisitor.VisitVeriTreeNode(self, node)

        self.assign = node

        self.TraverseNode(node.GetLValExpr())

        self.assign = 0

        self.TraverseNode(node.GetRValExpr())

    def VisitVeriBlockingAssign (self, node) :
        # Call of Base class Visit
        Verific.VeriPythonVisitor.VisitVeriStatement(self, node)

        self.assign = node

        self.TraverseNode(node.GetLVal())

        if ((node.OperType() != Verific.VERI_INC_OP) and (node.OperType() != Verific.VERI_DEC_OP)) :
            self.assign = 0

        self.TraverseNode(node.GetValue())

        self.assign = 0

        self.TraverseNode(node.GetControl())

    def VisitVeriNonBlockingAssign (self, node) :
        # Call of Base class Visit
        Verific.VeriPythonVisitor.VisitVeriStatement(self, node)

        self.assign = node

        self.TraverseNode(node.GetLVal())

        self.assign = 0

        self.TraverseNode(node.GetValue())

        self.TraverseNode(node.GetControl())

    def VisitVeriBinaryOperator (self, node) :
        if ((node.OperType() != Verific.VERI_INC_OP) and
            (node.OperType() != Verific.VERI_DEC_OP) and
            (node.OperType() != Verific.VERI_PLUS_ASSIGN) and
            (node.OperType() != Verific.VERI_MIN_ASSIGN) and
            (node.OperType() != Verific.VERI_MUL_ASSIGN) and
            (node.OperType() != Verific.VERI_DIV_ASSIGN) and
            (node.OperType() != Verific.VERI_MOD_ASSIGN) and
            (node.OperType() != Verific.VERI_AND_ASSIGN) and
            (node.OperType() != Verific.VERI_OR_ASSIGN) and
            (node.OperType() != Verific.VERI_XOR_ASSIGN) and
            (node.OperType() != Verific.VERI_LSHIFT_ASSIGN) and
            (node.OperType() != Verific.VERI_RSHIFT_ASSIGN) and
            (node.OperType() != Verific.VERI_ALSHIFT_ASSIGN) and
            (node.OperType() != Verific.VERI_ARSHIFT_ASSIGN) and
            (node.OperType() != Verific.VERI_EQUAL_ASSIGN)) :
            # Not interested in this binary operator:
            Verific.VeriPythonVisitor.VisitVeriBinaryOperator(self, node)
            return

        # Call of Base class Visit
        Verific.VeriPythonVisitor.VisitVeriExpression(self, node)

        self.assign = node

        # Traverse left operand
        self.TraverseNode(node.GetLeft())

        # Pre increment decrement has right expression set, so do not reset it here:
        if ((node.OperType() != Verific.VERI_INC_OP) and (node.OperType() != Verific.VERI_DEC_OP)) :
            self.assign = 0

        # Traverse right operand
        self.TraverseNode(node.GetRight())

        self.assign = 0


if (not Verific.veri_file_Analyze("test.v", Verific.veri_file.SYSTEM_VERILOG, "work", Verific.veri_file.SFCU)) :
    exit (1)

mod = Verific.veri_file.GetModule("prep5")
if (not mod) :
    exit (2)

mv = MyVisitor()
mv.Visit(mod)

exit (0)
 

Perl script:

#!/usr/bin/perl

use strict ;

push(@INC,"../pm") ;
require "Verific.pm" ;

package MyVisitor ;
    our @ISA = "Verific::VeriPerlVisitor" ;

    my $assign ;

    sub new {
        my ($class) = @_ ;
        my $self = $class->SUPER::new() ;
        $assign = 0 ;
        bless $self, $class ;
        return $self ;
    }

    sub VisitVeriIdRef : method {
        my ($self, $node) = @_ ;
        my $id = $node->GetId() ;
        if (!$id || !$assign) { return ; }

        my $str = $assign->GetPrettyPrintedString() ;
        $str =~ s/\n//g ;
        $node->Info("'" . $id->Name() . "' is assigned here as: " . $str) ;
    }
    sub VisitVeriSelectedName : method {
        my ($self, $node) = @_ ;
        my $id = $node->GetId() ;
        if (!$id || !$assign) { return ; }

        my $str = $assign->GetPrettyPrintedString() ;
        $str =~ s/\n//g ;
        my $my_name = $node->GetPrettyPrintedString() ;
        $node->Info("'" . $my_name . "' is assigned here as: " . $str) ;
    }
    sub VisitVeriIndexedId : method {
        my ($self, $node) = @_ ;

        # Call of Base class Visit
        $self->SUPER::VisitVeriName($node) ;

        $self->TraverseNode($node->GetPrefix()) ;

        # Do not traverse the index!
    }
    sub VisitVeriIndexedMemoryId : method {
        my ($self, $node) = @_ ;

        # Call of Base class Visit
        $self->SUPER::VisitVeriName($node) ;

        $self->TraverseNode($node->GetPrefix()) ;

        # Do not traverse the index!
    }
    sub VisitVeriNetRegAssign : method {
        my ($self, $node) = @_ ;

        # Call of Base class Visit
        $self->SUPER::VisitVeriTreeNode($node) ;

        $assign = $node ;

        $self->TraverseNode($node->GetLValExpr()) ;

        $assign = 0 ;

        $self->TraverseNode($node->GetRValExpr()) ;
    }
    sub VisitVeriBlockingAssign : method {
        my ($self, $node) = @_ ;

        # Call of Base class Visit
        $self->SUPER::VisitVeriStatement($node) ;

        $assign = $node ;

        $self->TraverseNode($node->GetLVal()) ;

        if (($node->OperType() != $Verific::VERI_INC_OP) && ($node->OperType() != $Verific::VERI_DEC_OP)) { $assign = 0 ; }

        $self->TraverseNode($node->GetValue()) ;

        $assign = 0 ;

        $self->TraverseNode($node->GetControl()) ;
    }
    sub VisitVeriNonBlockingAssign : method {
        my ($self, $node) = @_ ;

        # Call of Base class Visit
        $self->SUPER::VisitVeriStatement($node) ;

        $assign = $node ;

        $self->TraverseNode($node->GetLVal()) ;

        $assign = 0 ;

        $self->TraverseNode($node->GetValue()) ;

        $self->TraverseNode($node->GetControl()) ;
    }
    sub VisitVeriBinaryOperator : method {
        my ($self, $node) = @_ ;

        if (!(($node->OperType() == $Verific::VERI_INC_OP) ||
            ($node->OperType() == $Verific::VERI_DEC_OP) ||
            ($node->OperType() == $Verific::VERI_PLUS_ASSIGN) ||
            ($node->OperType() == $Verific::VERI_MIN_ASSIGN) ||
            ($node->OperType() == $Verific::VERI_MUL_ASSIGN) ||
            ($node->OperType() == $Verific::VERI_DIV_ASSIGN) ||
            ($node->OperType() == $Verific::VERI_MOD_ASSIGN) ||
            ($node->OperType() == $Verific::VERI_AND_ASSIGN) ||
            ($node->OperType() == $Verific::VERI_OR_ASSIGN) ||
            ($node->OperType() == $Verific::VERI_XOR_ASSIGN) ||
            ($node->OperType() == $Verific::VERI_LSHIFT_ASSIGN) ||
            ($node->OperType() == $Verific::VERI_RSHIFT_ASSIGN) ||
            ($node->OperType() == $Verific::VERI_ALSHIFT_ASSIGN) ||
            ($node->OperType() == $Verific::VERI_ARSHIFT_ASSIGN) ||
            ($node->OperType() == $Verific::VERI_EQUAL_ASSIGN))) {
            # Not interested in this binary operator:
            $self->SUPER::VisitVeriBinaryOperator($node) ;
            return ;
        }

        # Call of Base class Visit
        $self->SUPER::VisitVeriExpression($node) ;

        $assign = $node ;

        # Traverse left operand
        $self->TraverseNode($node->GetLeft()) ;

        # Pre increment decrement has right expression set, so do not reset it here:
        if (($node->OperType() != $Verific::VERI_INC_OP) && ($node->OperType() != $Verific::VERI_DEC_OP)) { $assign = 0 ; }

        # Traverse right operand
        $self->TraverseNode($node->GetRight()) ;

        $assign = 0 ;
    }

1 ;

if (!Verific::veri_file::Analyze("test.v", $Verific::veri_file::SYSTEM_VERILOG, "work", $Verific::veri_file::SFCU)) { exit (1) ; }

my $mod = Verific::veri_file::GetModule("prep5") ;
if (!$mod) { exit (2) ; }

my $mv = MyVisitor->new() ;
$mv->Visit($mod) ;

exit (0) ;
 

Verilog testcase:

     1	// PREP Benchmark 5, Arithmetic Circuit
     2	/* PREP5 contains a multiplier and accumulator
     3	Copyright (c) 1994 Synplicity, Inc.
     4	You may distribute freely, as long as this header remains attached. */
     5
     6	module prep5(Q, CLK, MAC, RST, A, B);
     7	  output [7:0] Q;
     8	  input CLK, MAC, RST;
     9	  input [3:0] A, B;
    10	  reg [7:0] Q;
    11	  wire [7:0] multiply_output;
    12	  wire [7:0] adder_output;
    13	  wire [7:0] func_out;
    14
    15	  assign multiply_output = A * B;
    16	  assign adder_output = MAC ? multiply_output + Q : multiply_output;
    17
    18	  always @(posedge CLK or posedge RST)
    19	    if (RST)
    20	      Q <= 0;
    21	    else
    22	      Q <= adder_output;
    23
    24	  assign func_out = fn1(0) ;
    25
    26	  function [7:0] fn1(input integer a) ;
    27	    integer b ;
    28	    b = a++ ;
    29	    b++ ;
    30	    ++b ;
    31	    b += (b = b+2) ;
    32	    return b ;
    33	  endfunction
    34	endmodule
 

Output:

-- Analyzing Verilog file 'test.v' (VERI-1482)
test.v(15): INFO: 'multiply_output' is assigned here as: multiply_output = (A * B)
test.v(16): INFO: 'adder_output' is assigned here as: adder_output = (MAC ? (multiply_output + Q) : multiply_output)
test.v(20): INFO: 'Q' is assigned here as: Q <=  0 ;
test.v(22): INFO: 'Q' is assigned here as: Q <=  adder_output ;
test.v(24): INFO: 'func_out' is assigned here as: func_out = fn1(0)
test.v(28): INFO: 'b' is assigned here as: b = (a ++ ) ;
test.v(28): INFO: 'a' is assigned here as: (a ++ )
test.v(29): INFO: 'b' is assigned here as: b ++  ;
test.v(30): INFO: 'b' is assigned here as:  ++ b ;
test.v(31): INFO: 'b' is assigned here as: b += (b = (b + 2)) ;
test.v(31): INFO: 'b' is assigned here as: (b = (b + 2))