目录


简介

VCS(版本控制系统)模式文件是一种定义如何识别和处理代码中特定模式的配置文件。这些模式可用于代码审查、文档生成、质量检查和IC设计验证等自动化任务。本指南详细介绍VCS配置模式的语法和使用方法。


模式文件语法

基本结构

VCS模式文件使用类似Perl的语法结构,主要由一个%PATTERNS_DEF哈希/字典结构组成:

1
2
3
4
5
6
7
8
9
10
11
%PATTERNS_DEF = (
Modes => {
ModeName1 => { 配置选项 },
ModeName2 => { 配置选项 },
// ...
},

Errors => { 错误模式定义 },
Warnings => { 警告模式定义 },
// ...
);

配置选项详解

每个模式可以包含以下配置选项:

  • Ignore: 是否忽略此模式(0为不忽略,1为忽略)。
  • Required: 是否必须匹配此模式(0为非必须,1为必须)。
  • StartString: 开始匹配的正则表达式。
  • EndString: 结束匹配的正则表达式。
  • RequiredText: 必须包含的文本模式数组。
  • OptionalText: 可选包含的文本模式数组。
  • ExcludedText: 不应包含的文本模式数组。
  • Actions: 匹配后要执行的操作。

常用模式示例

通用代码模式

CommentBlock - 注释块匹配

1
2
3
4
5
6
7
8
9
10
11
CommentBlock => {
Ignore => 0,
Required => 0,
StartString => '/\/\*/',
EndString => '/\*\//',
OptionalText => ['/\@brief/', '/\@param/', '/\@return/'],
Actions => {
Highlight => 'green',
Extract => 'documentation'
},
},

其他通用模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
CodeBlock => {
Ignore => 0,
Required => 0,
StartString => '/\{/',
EndString => '/\}/',
ExcludedText => ['/TODO:/', '/FIXME:/'],
Actions => {
Analyze => 'complexity',
Log => 1
},
},

TodoItem => {
Ignore => 0,
Required => 0,
StartString => '/TODO:|FIXME:/i',
EndString => '/$/',
RequiredText => ['/[\w\s]+/'],
Actions => {
Highlight => 'yellow',
Extract => 'tasks',
Priority => 'high'
},
},

IC设计验证专用模式

时钟域识别 (ClockDomain)

1
2
3
4
5
6
7
8
9
10
11
12
ClockDomain => {
Ignore => 0,
Required => 1,
StartString => '/\bclock\s+domain\b|\bclk_domain\b/i',
EndString => '/;/',
RequiredText => ['/\b(posedge|negedge)\b/', '/\bclk\b/'],
Actions => {
Highlight => 'purple',
Extract => 'timing_info',
Log => 1
},
},

属性断言 (AssertProperty)

1
2
3
4
5
6
7
8
9
10
11
12
13
AssertProperty => {
Ignore => 0,
Required => 0,
StartString => '/\b(assert\s+property|property\s+[a-zA-Z_][a-zA-Z0-9_]*)/i',
EndString => '/\bendproperty\b|\);/i',
RequiredText => ['/\b(always|never|eventually)\b/i'],
OptionalText => ['/\b(sequence|strong|weak)\b/i'],
Actions => {
Highlight => 'blue',
Extract => 'assertions',
Verify => 'safety_property'
},
},

工具与脚本

如何调用PAT模式文件

PAT模式文件可以通过多种方式被调用和使用,下面介绍几种常见的调用方法:

1. 命令行直接调用

使用VCS命令行工具直接引用PAT文件:

1
vcs -full64 -sverilog +pat+c:/prj/example.pat -f filelist.f -l compile.log

关键参数说明:

  • +pat+<filepath> - 指定PAT模式文件的路径
  • -f filelist.f - 包含要处理的源文件列表
  • -l compile.log - 指定日志输出文件

2. 通过配置文件引用

在VCS配置文件中引用PAT文件:

1
2
3
// vcs.config
PATTERNS_FILE = "c:/prj/example.pat";
PATTERN_MODE = "ClockDomain,AssertProperty"; // 可以指定要使用的模式

然后在命令行中引用此配置文件:

1
vcs -full64 -sverilog -config vcs.config -f filelist.f

3. 通过脚本间接调用

使用自定义Perl脚本调用处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/perl
# 示例:通过脚本调用PAT模式验证

use strict;
use warnings;
use VCS::PatternChecker; # VCS Pattern API模块

my $checker = VCS::PatternChecker->new(
pattern_file => 'c:/prj/example.pat',
active_modes => ['CommentBlock', 'ClockDomain'],
report_file => 'pattern_report.txt'
);

$checker->check_files('src/*.sv');
$checker->print_summary();

4. 与VCS GUI工具集成

在VCS的GUI环境中:

  1. 打开VCS图形界面:vcs -gui
  2. 选择菜单:Tools > Code Quality > Pattern Checker
  3. 在弹出窗口中,浏览并选择PAT文件
  4. 选择要检查的源文件目录
  5. 选择要应用的模式(可多选)
  6. 点击”Run”开始验证

5. 与项目Makefile集成

在项目Makefile中定义PAT验证规则:

1
2
3
4
5
# Pattern verification target
pattern_check:
@echo "Running pattern verification..."
@vcs -full64 -sverilog +pat+$(PATTERN_FILE) -f $(SRC_LIST) -l pattern.log
@grep -E "Error|Warning" pattern.log || echo "Pattern check passed."

然后使用make pattern_check执行验证。

注释检查脚本

以下是check_comments.pl脚本的完整代码,用于检查代码库中的注释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#!/usr/bin/perl
# filepath: c:\prj\check_comments.pl
use strict;
use warnings;
use Getopt::Long;
use File::Find;

# 命令行参数
my $pattern_name = '';
my $src_pattern = '';
my $verbose = 0;
my $help = 0;

GetOptions(
"pattern=s" => \$pattern_name,
"src=s" => \$src_pattern,
"verbose" => \$verbose,
"help" => \$help
);

if ($help || !$pattern_name || !$src_pattern) {
print_help();
exit(0);
}

# 加载模式定义
my %PATTERNS_DEF = load_patterns();

# 检查模式是否存在
die "Error: Pattern '$pattern_name' not found.\n"
unless exists $PATTERNS_DEF{Modes}{$pattern_name};

my $pattern = $PATTERNS_DEF{Modes}{$pattern_name};
print "Using pattern: $pattern_name\n" if $verbose;

# 扫描文件
my @files = glob($src_pattern);
foreach my $file (@files) {
open my $fh, '<', $file or die "Cannot open $file: $!";
my $content = do { local $/; <$fh> };
close $fh;

if ($content =~ /$pattern->{StartString}/) {
print "Found pattern in $file\n";
} else {
print "No pattern found in $file\n";
}
}

sub load_patterns {
return (
Modes => {
CommentBlock => {
StartString => '/\/\*/',
EndString => '/\*\//',
RequiredText => ['/\@brief/', '/\@param/', '/\@return/'],
},
}
);
}

sub print_help {
print <<HELP;
Usage: perl check_comments.pl --pattern=PATTERN --src=SOURCE_FILES [options]

Options:
--pattern=PATTERN 指定要使用的模式名称
--src=SOURCE_FILES 源文件路径,支持通配符
--verbose 显示详细输出
--help 显示帮助信息
HELP
}

最佳实践

  1. 针对特定语言调整正则表达式:根据语言特性优化StartString和EndString。
  2. 从宽松规则开始:逐步收紧规则以减少误报。
  3. 结合工具使用:将模式文件与脚本结合使用,自动化代码审查和验证。

完整示例

以下是一个完整的模式文件示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
%PATTERNS_DEF = (
Modes => {
CommentBlock => {
Ignore => 0,
Required => 0,
StartString => '/\/\*/',
EndString => '/\*\//',
OptionalText => ['/\@brief/', '/\@param/', '/\@return/'],
Actions => {
Highlight => 'green',
Extract => 'documentation'
},
},
// ...
}
);

通过结合模式文件和脚本,您可以实现自动化的代码审查、文档生成和IC设计验证。

This is a sample Python script.

Press Shift+F10 to execute it or replace it with your code.

Press Double Shift to search everywhere for classes, files, tool windows, actions, and settings.

-- coding: UTF-8 --

import os

import sys
import re

配置

target_dir =”xxx” # 替换为你要扫描的目录
output_file = “verilog_comparison_results.txt”

定义比较操作符

#comparison_ops = [‘>’, ‘>=’, ‘==’, ‘<=’, ‘<’]
#pattern = r’if\s(\s*([a-zA-Z_][a-zA-Z0-9_])\s(>=|<=|>|<|==)\s*(\d+)\s*)‘
pattern = r’([a-zA-Z_][a-zA-Z0-9_]*)\s*(>=|<=|>|<)\s
(\d+)’

正则表达式:匹配只包含 “var <= 10” 的行(如 assign var <= 10;)

#single_compare_pattern = r’^\s*([a-zA-Z_][a-zA-Z0-9_])\s(>=|<=|>|<|==)\s*(\d+|1'b0|2'b0|3'b0|4'b0|5'b0|6'b0|7'b0|8'b0|9'b0|10'b0|11'b0|12'b0|13'b0|14'b0|5'b0|6'b0|7'b0|8'b0|9'b0|10'b0|16'b0|32'b0|9'd20|64'h0|1'b1|2'b1|3'b1|16'b1|8'b1|9'd20|64'h1|1'd0|2'd0|3'd0|4'd0|8'd0|16'd0|32'd0|1'h0|2'h0|3'h0|4'h0|8'h0|16'h0|32'h0|9'd20)\s*;$’
single_compare_pattern =r’^\s*([a-zA-Z_][a-zA-Z0-9_])\s*(>=|<=|>|<|==)\s(\d+|(\d+)'[a-zA-Z]+[0-9'])\s;$’

打开输出文件

with open(output_file, ‘w’, encoding=’utf-8’) as f_out:
# 遍历目录中的所有文件
for root, dirs, files in os.walk(target_dir):
for file in files:
# 如果文件名包含 “ecc”,跳过
if “ecc” in file:
continue
if “host” in file:
continue
if “ssu” in file:
continue
if file.endswith(“.v”) or file.endswith(“.sv”): # 只处理 Verilog 和 SystemVerilog 文件
file_path = os.path.join(root, file)
try:
with open(file_path, ‘r’, encoding=’utf-8’) as f_in: # 尝试用 utf-8 读取
lines = f_in.readlines()
except UnicodeDecodeError:
# 如果 utf-8 失败,尝试用 gbk 或其他编码
with open(file_path, ‘r’, encoding=’gbk’) as f_in:
lines = f_in.readlines()
for line_num, line in enumerate(lines, start=1):
# 使用正则表达式匹配条件判断语句中的变量与常值比较
if line.strip().startswith(“for”):
continue
if line.strip().startswith(“//“):
continue
if line.strip().startswith(“assign”):
continue
if line.strip().startswith(“wire”):
continue
if line.strip().startswith(“4’b”):
continue
if line.strip().startswith(“3’b”):
continue
if line.strip().startswith(“default”):
continue
if line.strip().startswith(“input”):
continue
if line.strip().startswith(“generate”):
continue
if line.strip().startswith(“lan”):
continue
if line.strip().startswith(“4’h”):
continue
if line.strip().startswith(“oxu_ibe_inq_chn”):
continue
matches = re.finditer(pattern, line)
for match in matches:
var_name = match.group(1)
op = match.group(2)
value = match.group(3)
# 构造匹配的完整语句
matched_line = line.strip()
# 检查该行是否只包含 “var <= 10”
if re.fullmatch(single_compare_pattern, line.strip()):
# 如果只包含变量与常值比较,不输出
continue
# 否则,输出
f_out.write(f”文件: {file_path}\n”)
f_out.write(f”行号: {line_num}\n”)
f_out.write(f”代码: {matched_line}\n”)
f_out.write(f”变量: {var_name} {op} {value}\n”)
f_out.write(“-“ * 40 + “\n”)
print(f”结果已保存到 {output_file}”)