如何使用 Perl 解析 Verilog 代码来识别正在分配的寄存器?

How to use Perl to parse Verilog code to identify registers being assigned?

======== 2016 年 2 月 1 日编辑 ========

最终在匿名小伙伴的帮助下解决了问题:

while ($verilog_code =~ /\/\/.*?\n|(\w+)\s*<=\s*.*?;/smgx) {
        if(defined ) {
                push (@regs, );
                }
        }

foreach (0..$#regs) {
        print "\@regs[$_]= @regs[$_]\n";
        }

使用两个匹配项:一个用于评论,"jumps over" 不需要的评论;赋值语法的另一个匹配项,它捕获了我想要的东西。结果如下,是我所期望的:

@regs[0]= ak47
@regs[1]= ak48
@regs[2]= ak49
@regs[3]= ak50
@regs[4]= ak53
@regs[5]= ak54

再次感谢大家:)

======== 以下为原文post ========

我在 Whosebug 上的第一个 post,感谢任何可能提供帮助的人。

我正在使用 Perl 解析 Verilog 源代码并识别块中分配的所有寄存器。 Verilog 赋值语法类似于“reg_data <= din ;”,意思是 "reg_data is assigned the value of din".

随着Verilog代码越来越复杂,Perl解析也越来越困难。例如(在 Perl 变量 $verilog_code 中定义):

my $verilog_code = "
if (s1) ak47 <= din; ak48
        <= d48; // my comment
else if (s2) begin
        // comments
        ak49 <= d49; ak50 <= d50; // ak51 <= d51 ; ak52 <= d52; // comments
        ak53 <=
                d5+d3; 
        end
else ak54 <= ((d<=e) ? (d540) : d541) ; 
        // comment: ak55 <= d55 ; 
";

其中涵盖了一些具体案例:

  1. 一行代码可能包含许多赋值,例如。 ak47和ak48共线,ak49和ak50共线等
  2. 一个作业可能跨越多行,例如。 ak48和ak53都占两行
  3. 以“//”开头的行注释可能出现在行首或行中;评论中的任何内容都应被忽略。

因此,对于 $verilog_code,ak47、ak48、ak49、ak50、ak53 和 ak54 的分配应该被识别,而 ak51、ak52 和 ak55 应该被忽略。我做了很多尝试,使用 Perl 正则表达式 (regex) 来解析文本,但到目前为止 none 成功了:

@reg_assignments = $verilog_code =~ m/(?<!\/\/\s*)(\w+\s*<=.*?;)/sg;

====> 这个报错 "Variable length lookbehind not implemented in regex".

@reg_assignments = $verilog_code =~ m/(?<!\/\/ )(\b\w+\b\s*<=.*?;)/sg;

====> 这个得到了以下结果:

@reg_assignments[0]= ak47 <= din;
@reg_assignments[1]= ak48
        <= d48;
@reg_assignments[2]= ak49 <= d49;
@reg_assignments[3]= ak50 <= d50;
@reg_assignments[4]= ak52 <= d52;
@reg_assignments[5]= ak53 <=
                d5+d3;
@reg_assignments[6]= ak54 <= ((d<=e) ? (d540) : d541) ;
@reg_assignments[7]= ak55 <= d55 ;

----> 这里成功忽略了ak51的赋值,但是错误捕获了ak52和ak55。

@reg_assignments = $verilog_code =~ m/(?<!\/\/)(?:[^\n]*?(\b\w+\s*<=.*?;))/sg ;

====> 这一个得到以下结果:

@reg_assignments[0]= ak47 <= din;
@reg_assignments[1]= ak48
        <= d48;
@reg_assignments[2]= ak49 <= d49;
@reg_assignments[3]= ak50 <= d50;
@reg_assignments[4]= ak51 <= d51 ;
@reg_assignments[5]= ak52 <= d52;
@reg_assignments[6]= ak53 <=
                d5+d3;
@reg_assignments[7]= ak54 <= ((d<=e) ? (d540) : d541) ;
@reg_assignments[8]= ak55 <= d55 ;

我只想提取Verilog文本中所有有效的赋值,不包括注释中的东西。解决方案一直困扰着我好几天了。谁能给我任何关于如何实现这个的提示?谢谢~

如果您的 verilog 代码真的那么简单并且您不想安装某些模块并且性能并不重要,那么我建议两三个步骤:

  • 删除任何评论:

    $verilog_code =~ s:^(.*?)//.*$::mg;

  • 可选:去掉换行符:

    $verilog_code =~ s/\n//g;

  • 获取作业:

    my @assignments = ($verilog_code =~ /\S+\s*<=[^;]+;/g);

    这都是非空白,接着是<=直到下一个;

这给出了

@assignments = (
                 'ak47 <= din;',
                 'ak48        <= d48;',
                 'ak49 <= d49;',
                 'ak50 <= d50;',
                 'ak53 <=                d5+d3;',
                 'ak54 <= ((d<=e) ? (d540) : d541) ;'
               );

根据@Greg 的建议,我只是将解决方案发布在这里,以防其他人可能需要它。非常感谢花时间帮助我的 Perl Dog :-)

======== 2016 年 2 月 1 日编辑 ========

最终在匿名小伙伴的帮助下解决了问题:

while ($verilog_code =~ /\/\/.*?\n|(\w+)\s*<=\s*.*?;/smgx) {
        if(defined ) {
                push (@regs, );
                }
        }

foreach (0..$#regs) {
        print "\@regs[$_]= @regs[$_]\n";
        }

使用两个匹配项:一个用于评论,"jumps over" 不需要的评论;赋值语法的另一个匹配项,它捕获了我想要的东西。结果如下,符合我的预期:

@regs[0]= ak47
@regs[1]= ak48
@regs[2]= ak49
@regs[3]= ak50
@regs[4]= ak53
@regs[5]= ak54

再次感谢大家:)