124 lines
4.6 KiB
Python
124 lines
4.6 KiB
Python
import sys
|
||
import re
|
||
import os
|
||
|
||
class CoverageAnalyzer:
|
||
def __init__(self, annotated_file_path):
|
||
self.file_path = annotated_file_path
|
||
self.source_code = self._load_file()
|
||
self.module_name = self._extract_module_name()
|
||
self.coverage_score = 0.0
|
||
self.missing_blocks = []
|
||
|
||
def _load_file(self):
|
||
"""加载文件内容,处理异常"""
|
||
if not os.path.exists(self.file_path):
|
||
raise FileNotFoundError(f"Cannot find file: {self.file_path}")
|
||
with open(self.file_path, 'r', encoding='utf-8') as f:
|
||
return f.readlines()
|
||
|
||
def _extract_module_name(self):
|
||
"""通用化步骤1:自动从代码中提取 module name"""
|
||
for line in self.source_code:
|
||
match = re.search(r'module\s+(\w+)', line)
|
||
if match:
|
||
return match.group(1)
|
||
return "unknown_module"
|
||
|
||
def analyze(self):
|
||
"""核心分析逻辑:计算分数并提取未覆盖块"""
|
||
total_lines = 0
|
||
covered_lines = 0
|
||
current_block = []
|
||
recording = False
|
||
|
||
for i, line in enumerate(self.source_code):
|
||
# === [修复点] 安检门:如果行是空的,直接跳过 ===
|
||
if not line.strip():
|
||
continue
|
||
|
||
# 判定行类型
|
||
is_missed = line.startswith('%000000') or line.strip().startswith('#')
|
||
is_covered = line.startswith('%') and not is_missed
|
||
|
||
# 统计分数
|
||
if is_missed or is_covered:
|
||
total_lines += 1
|
||
if is_covered:
|
||
covered_lines += 1
|
||
|
||
# === [修复点] 安全切分:防止切割失败 ===
|
||
parts = line.split(maxsplit=1)
|
||
if len(parts) < 2:
|
||
# 如果这一行切不出两部分(比如只有标号没有代码),也跳过
|
||
continue
|
||
|
||
clean_code = parts[-1].strip()
|
||
# 过滤掉非实质性代码
|
||
is_substantive = len(clean_code) > 3 and not clean_code.startswith("//")
|
||
|
||
if is_missed and is_substantive:
|
||
if not recording:
|
||
recording = True
|
||
# 添加上下文 (尝试找前一行非空行作为上下文比较复杂,这里简化处理)
|
||
if i > 0 and self.source_code[i-1].strip():
|
||
prev_parts = self.source_code[i-1].split(maxsplit=1)
|
||
if len(prev_parts) >= 2:
|
||
prev_code = prev_parts[-1].strip()
|
||
current_block.append(f"Line {i}: {prev_code} ...")
|
||
current_block.append(f"Line {i+1}: {clean_code} <--- MISSING")
|
||
else:
|
||
if recording:
|
||
recording = False
|
||
self.missing_blocks.append(current_block)
|
||
current_block = []
|
||
|
||
# 计算最终分数
|
||
if total_lines > 0:
|
||
self.coverage_score = (covered_lines / total_lines) * 100.0
|
||
|
||
return self.coverage_score
|
||
|
||
def generate_prompt(self):
|
||
"""通用化步骤2:使用模板生成 Prompt"""
|
||
prompt_template = """
|
||
You are an expert FPGA verification engineer.
|
||
I am verifying a Verilog module named '{module_name}'.
|
||
The current testbench achieves only {score:.2f}% coverage.
|
||
The following logic blocks are NEVER executed during simulation:
|
||
|
||
{missing_logic_str}
|
||
|
||
[Task]
|
||
Please write a NEW SystemVerilog task to target these specific missing scenarios.
|
||
1. Analyze WHY these lines are not executed (e.g., specific counter value, rare state transition).
|
||
2. Generate a task named 'test_coverage_fix' that drives inputs to trigger these conditions.
|
||
3. Return ONLY the code block.
|
||
"""
|
||
blocks_str = ""
|
||
for idx, block in enumerate(self.missing_blocks[:4]):
|
||
blocks_str += f"--- Missing Scenario {idx+1} ---\n"
|
||
blocks_str += "\n".join(block)
|
||
blocks_str += "\n\n"
|
||
|
||
if not blocks_str:
|
||
blocks_str = "(No significant missing logic found. Coverage is likely high.)"
|
||
|
||
return prompt_template.format(
|
||
module_name=self.module_name,
|
||
score=self.coverage_score,
|
||
missing_logic_str=blocks_str
|
||
)
|
||
|
||
if __name__ == "__main__":
|
||
if len(sys.argv) < 2:
|
||
print("Usage: python3 coverage_agent.py <annotated_file_path>")
|
||
sys.exit(1)
|
||
|
||
agent = CoverageAnalyzer(sys.argv[1])
|
||
score = agent.analyze()
|
||
|
||
print(f"--- Analysis Report for {agent.module_name} ---")
|
||
print(f"Score: {score:.2f}%")
|
||
print("-" * 30)
|
||
print(agent.generate_prompt()) |