1454 lines
63 KiB
Python
1454 lines
63 KiB
Python
|
|
# # #第四版
|
|||
|
|
|
|||
|
|
# # """
|
|||
|
|
# # Description : Coverage-Guided Agent (CGA) Main Controller
|
|||
|
|
# # - Integrated with Layer 0: Semantic Analysis
|
|||
|
|
# # - Integrated with Layer 1: Diversity Constraint Injection
|
|||
|
|
# # - Integrated with Layer 4: Energy Allocation
|
|||
|
|
# # Author : CorrectBench Integration
|
|||
|
|
# # """
|
|||
|
|
# # import os
|
|||
|
|
# # import sys
|
|||
|
|
# # import shutil
|
|||
|
|
|
|||
|
|
# # import LLM_call as llm
|
|||
|
|
# # import loader_saver as ls
|
|||
|
|
# # from loader_saver import autologger as logger
|
|||
|
|
# # from utils.verilator_call import verilator_run_coverage
|
|||
|
|
# # from autoline.cga_utils import CoverageParser, TBInjector
|
|||
|
|
# # # [新增] 导入语义分析层
|
|||
|
|
# # from autoline.semantic_analyzer import SemanticAnalyzer, FunctionPointType
|
|||
|
|
# # # [新增] 导入能量分配层
|
|||
|
|
# # from autoline.energy_allocator import EnergyAllocator, EnergyState
|
|||
|
|
# # # [新增] 导入多样性约束注入器
|
|||
|
|
# # from autoline.diversity_injector import DiversityInjector
|
|||
|
|
# # # [新增] 导入测试历史管理器
|
|||
|
|
# # from autoline.test_history import TestHistoryManager
|
|||
|
|
|
|||
|
|
# # class TaskTBCGA:
|
|||
|
|
# # def __init__(self, task_dir, task_id, header, DUT_code, TB_code, config):
|
|||
|
|
# # self.task_dir = task_dir
|
|||
|
|
# # self.task_id = task_id
|
|||
|
|
# # self.header = header
|
|||
|
|
# # self.DUT_code = DUT_code
|
|||
|
|
# # self.TB_code = TB_code
|
|||
|
|
# # self.config = config
|
|||
|
|
|
|||
|
|
# # self.max_iter = 5
|
|||
|
|
# # self.target_coverage = 95.0
|
|||
|
|
# # self.model = config.gpt.model
|
|||
|
|
|
|||
|
|
# # self.best_tb = TB_code
|
|||
|
|
# # self.best_score = 0.0
|
|||
|
|
|
|||
|
|
# # # [新增] 能量分配器
|
|||
|
|
# # self.energy_allocator: EnergyAllocator = None
|
|||
|
|
# # # [新增] 多样性约束注入器
|
|||
|
|
# # self.diversity_injector: DiversityInjector = None
|
|||
|
|
|
|||
|
|
# # # [新增辅助函数] 从父目录拷贝 DUT
|
|||
|
|
# # def _prepare_dut(self, target_dir):
|
|||
|
|
# # source_dut = os.path.join(self.task_dir, "DUT.v")
|
|||
|
|
# # target_dut = os.path.join(target_dir, "DUT.v")
|
|||
|
|
|
|||
|
|
# # # 优先拷贝现有的文件
|
|||
|
|
# # if os.path.exists(source_dut):
|
|||
|
|
# # shutil.copy(source_dut, target_dut)
|
|||
|
|
# # else:
|
|||
|
|
# # # 只有当文件由于某种原因被删除了,才降级使用内存中的 code
|
|||
|
|
# # ls.save_code(self.DUT_code, target_dut)
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
# # def run(self):
|
|||
|
|
# # logger.info(f"[{self.task_id}] Starting Coverage-Guided Agent (CGA)...")
|
|||
|
|
|
|||
|
|
# # # 1. 确保工作目录存在 (saves/任务名/5_CGA)
|
|||
|
|
# # work_dir = os.path.join(self.task_dir, "5_CGA")
|
|||
|
|
# # if os.path.exists(work_dir):
|
|||
|
|
# # shutil.rmtree(work_dir)
|
|||
|
|
# # os.makedirs(work_dir, exist_ok=True)
|
|||
|
|
|
|||
|
|
# # # === [新增] Step 0: 语义分析 ===
|
|||
|
|
# # logger.info(f"[{self.task_id}] Running Semantic Analysis (Layer 0)...")
|
|||
|
|
# # self.semantic_result = None
|
|||
|
|
# # try:
|
|||
|
|
# # semantic_analyzer = SemanticAnalyzer(self.DUT_code)
|
|||
|
|
# # self.semantic_result = semantic_analyzer.analyze()
|
|||
|
|
|
|||
|
|
# # # 记录分析结果摘要
|
|||
|
|
# # fp_count = len(self.semantic_result.get('function_points', []))
|
|||
|
|
# # fsm_info = semantic_analyzer.get_fsm_info()
|
|||
|
|
# # if fsm_info:
|
|||
|
|
# # logger.info(f" FSM detected: {fsm_info.get('state_variable', 'unknown')} "
|
|||
|
|
# # f"({len(fsm_info.get('states', []))} states)")
|
|||
|
|
# # logger.info(f" Total function points identified: {fp_count}")
|
|||
|
|
|
|||
|
|
# # # 保存语义分析报告
|
|||
|
|
# # semantic_report = semantic_analyzer.generate_prompt_context()
|
|||
|
|
# # ls.save_code(semantic_report, os.path.join(work_dir, "semantic_analysis.txt"))
|
|||
|
|
|
|||
|
|
# # # === [新增] Step 0.1: 初始化能量分配器 ===
|
|||
|
|
# # if self.semantic_result.get('function_points'):
|
|||
|
|
# # self.energy_allocator = EnergyAllocator(max_iterations=self.max_iter)
|
|||
|
|
# # energy_init_result = self.energy_allocator.initialize(
|
|||
|
|
# # self.semantic_result['function_points']
|
|||
|
|
# # )
|
|||
|
|
# # logger.info(f" Energy allocator initialized: {energy_init_result['targets']} targets")
|
|||
|
|
|
|||
|
|
# # # === [新增] Step 0.2: 初始化多样性约束注入器 ===
|
|||
|
|
# # history_file = os.path.join(work_dir, "test_history.json")
|
|||
|
|
# # # 创建 TestHistoryManager 并传递 history_file
|
|||
|
|
# # history_manager = TestHistoryManager(history_file=history_file)
|
|||
|
|
# # self.diversity_injector = DiversityInjector(history_manager=history_manager)
|
|||
|
|
# # logger.info(f" Diversity injector initialized with history file: {history_file}")
|
|||
|
|
|
|||
|
|
# # except Exception as e:
|
|||
|
|
# # logger.warning(f"Semantic analysis failed: {e}. Continuing without semantic guidance.")
|
|||
|
|
# # # ================================
|
|||
|
|
|
|||
|
|
# # current_tb = self.TB_code
|
|||
|
|
# # last_annotated_file = None
|
|||
|
|
|
|||
|
|
# # # --- Baseline ---
|
|||
|
|
# # logger.info(f"--- CGA Iter 0 (Baseline) ---")
|
|||
|
|
# # iter0_dir = os.path.join(work_dir, "iter_0")
|
|||
|
|
# # os.makedirs(iter0_dir, exist_ok=True)
|
|||
|
|
|
|||
|
|
# # self._prepare_dut(iter0_dir)
|
|||
|
|
# # ls.save_code(current_tb, os.path.join(iter0_dir, "driver.v"))
|
|||
|
|
|
|||
|
|
# # success, score, annotated_path = verilator_run_coverage(iter0_dir, "DUT.v", "driver.v")
|
|||
|
|
|
|||
|
|
# # self.best_score = score
|
|||
|
|
# # self.best_tb = current_tb
|
|||
|
|
# # last_annotated_file = annotated_path
|
|||
|
|
|
|||
|
|
# # logger.info(f"Baseline Coverage: {score:.2f}%")
|
|||
|
|
|
|||
|
|
# # if score >= self.target_coverage:
|
|||
|
|
# # logger.success(f"Target reached at baseline!")
|
|||
|
|
# # # [修改] 返回元组 (代码, 分数)
|
|||
|
|
# # return self.best_tb, self.best_score
|
|||
|
|
|
|||
|
|
# # # --- Loop ---
|
|||
|
|
# # for i in range(1, self.max_iter + 1):
|
|||
|
|
# # logger.info(f"--- CGA Iter {i} / {self.max_iter} ---")
|
|||
|
|
|
|||
|
|
# # # === [新增] 能量检查:是否还有活跃目标 ===
|
|||
|
|
# # if self.energy_allocator:
|
|||
|
|
# # current_target = self.energy_allocator.select_next_target()
|
|||
|
|
# # if not current_target:
|
|||
|
|
# # logger.info("No more active targets with remaining energy. Stopping.")
|
|||
|
|
# # break
|
|||
|
|
# # logger.info(f"Target: {current_target}")
|
|||
|
|
# # # =========================================
|
|||
|
|
|
|||
|
|
# # if not last_annotated_file: break
|
|||
|
|
|
|||
|
|
# # # [修改] 传递语义分析结果、能量分配器、多样性注入器给 CoverageParser
|
|||
|
|
# # parser = CoverageParser(
|
|||
|
|
# # last_annotated_file,
|
|||
|
|
# # tb_code=self.best_tb,
|
|||
|
|
# # semantic_result=self.semantic_result,
|
|||
|
|
# # energy_allocator=self.energy_allocator,
|
|||
|
|
# # diversity_injector=self.diversity_injector # [新增]
|
|||
|
|
# # )
|
|||
|
|
# # prompt = parser.generate_prompt(self.best_score)
|
|||
|
|
|
|||
|
|
# # if not prompt:
|
|||
|
|
# # logger.info("No reachable missing blocks found. Stopping.")
|
|||
|
|
# # break
|
|||
|
|
|
|||
|
|
# # logger.info(f"Asking LLM to fix missing logic (Current: {self.best_score:.2f}%)...")
|
|||
|
|
# # messages = [{"role": "user", "content": prompt}]
|
|||
|
|
|
|||
|
|
# # try:
|
|||
|
|
# # response, _ = llm.llm_call(messages, self.model)
|
|||
|
|
# # codes = llm.extract_code(response, "verilog")
|
|||
|
|
# # new_task_code = codes[0] if codes else ""
|
|||
|
|
# # if not new_task_code:
|
|||
|
|
# # # [新增] 记录失败
|
|||
|
|
# # if self.energy_allocator:
|
|||
|
|
# # self.energy_allocator.record_generation(
|
|||
|
|
# # success=False,
|
|||
|
|
# # coverage_delta=0.0,
|
|||
|
|
# # energy_cost=1.0
|
|||
|
|
# # )
|
|||
|
|
# # continue
|
|||
|
|
# # except Exception as e:
|
|||
|
|
# # logger.error(f"LLM Call failed: {e}")
|
|||
|
|
# # # [新增] 记录失败
|
|||
|
|
# # if self.energy_allocator:
|
|||
|
|
# # self.energy_allocator.record_generation(
|
|||
|
|
# # success=False,
|
|||
|
|
# # coverage_delta=0.0,
|
|||
|
|
# # energy_cost=1.0
|
|||
|
|
# # )
|
|||
|
|
# # break
|
|||
|
|
|
|||
|
|
# # injector = TBInjector(self.best_tb)
|
|||
|
|
# # enhanced_tb = injector.inject(new_task_code, iter_idx=i)
|
|||
|
|
|
|||
|
|
# # iter_dir = os.path.join(work_dir, f"iter_{i}")
|
|||
|
|
# # os.makedirs(iter_dir, exist_ok=True)
|
|||
|
|
|
|||
|
|
# # self._prepare_dut(iter_dir)
|
|||
|
|
# # ls.save_code(enhanced_tb, os.path.join(iter_dir, "driver.v"))
|
|||
|
|
|
|||
|
|
# # success, new_score, new_annotated_path = verilator_run_coverage(iter_dir, "DUT.v", "driver.v")
|
|||
|
|
|
|||
|
|
# # # === [新增] 记录生成结果到能量分配器 ===
|
|||
|
|
# # coverage_delta = new_score - self.best_score if success else 0.0
|
|||
|
|
# # generation_success = success and new_score > self.best_score
|
|||
|
|
|
|||
|
|
# # if self.energy_allocator:
|
|||
|
|
# # self.energy_allocator.record_generation(
|
|||
|
|
# # success=generation_success,
|
|||
|
|
# # coverage_delta=coverage_delta,
|
|||
|
|
# # energy_cost=1.0
|
|||
|
|
# # )
|
|||
|
|
# # # =========================================
|
|||
|
|
|
|||
|
|
# # # === [新增] 记录测试用例到多样性历史 ===
|
|||
|
|
# # if self.diversity_injector:
|
|||
|
|
# # # 提取已知信号
|
|||
|
|
# # known_signals = []
|
|||
|
|
# # if self.semantic_result:
|
|||
|
|
# # known_signals = [p.get('name', '') for p in self.semantic_result.get('ports', [])]
|
|||
|
|
|
|||
|
|
# # self.diversity_injector.record_test(
|
|||
|
|
# # code=new_task_code,
|
|||
|
|
# # target_function=self.energy_allocator.current_target.function_point if self.energy_allocator and self.energy_allocator.current_target else "",
|
|||
|
|
# # coverage_score=new_score,
|
|||
|
|
# # success=generation_success,
|
|||
|
|
# # iteration=i,
|
|||
|
|
# # known_signals=known_signals
|
|||
|
|
# # )
|
|||
|
|
# # # =======================================
|
|||
|
|
|
|||
|
|
# # if success and new_score > self.best_score:
|
|||
|
|
# # improvement = new_score - self.best_score
|
|||
|
|
# # logger.success(f"Coverage Improved! +{improvement:.2f}% ({self.best_score:.2f}% -> {new_score:.2f}%)")
|
|||
|
|
# # self.best_score = new_score
|
|||
|
|
# # self.best_tb = enhanced_tb
|
|||
|
|
# # last_annotated_file = new_annotated_path
|
|||
|
|
# # elif success and new_score == self.best_score:
|
|||
|
|
# # logger.info(f"Coverage unchanged. Keeping previous.")
|
|||
|
|
# # else:
|
|||
|
|
# # logger.warning(f"Regression or Failure. Discarding changes.")
|
|||
|
|
|
|||
|
|
# # if self.best_score >= self.target_coverage:
|
|||
|
|
# # logger.success("Target coverage reached!")
|
|||
|
|
# # break
|
|||
|
|
|
|||
|
|
# # logger.info(f"CGA Finished. Final Coverage: {self.best_score:.2f}%")
|
|||
|
|
|
|||
|
|
# # # === [新增] 生成能量分配报告 ===
|
|||
|
|
# # if self.energy_allocator:
|
|||
|
|
# # energy_report = self.energy_allocator.generate_report()
|
|||
|
|
# # ls.save_code(energy_report, os.path.join(work_dir, "energy_report.txt"))
|
|||
|
|
# # logger.info(f"Energy report saved to {work_dir}/energy_report.txt")
|
|||
|
|
# # # =================================
|
|||
|
|
|
|||
|
|
# # # === [新增] 生成多样性报告并保存历史 ===
|
|||
|
|
# # if self.diversity_injector:
|
|||
|
|
# # diversity_report = self.diversity_injector.generate_diversity_report()
|
|||
|
|
# # ls.save_code(diversity_report, os.path.join(work_dir, "diversity_report.txt"))
|
|||
|
|
# # logger.info(f"Diversity report saved to {work_dir}/diversity_report.txt")
|
|||
|
|
|
|||
|
|
# # # 保存测试历史
|
|||
|
|
# # self.diversity_injector.history.save()
|
|||
|
|
# # # ======================================
|
|||
|
|
|
|||
|
|
# # # [修改] 返回元组 (代码, 分数)
|
|||
|
|
# # return self.best_tb, self.best_score
|
|||
|
|
|
|||
|
|
# #终版
|
|||
|
|
# """
|
|||
|
|
# Description : Coverage-Guided Agent (CGA) Main Controller
|
|||
|
|
# - Integrated with Layer 0: Semantic Analysis
|
|||
|
|
# - Integrated with Layer 1: Diversity Constraint Injection
|
|||
|
|
# - Integrated with Layer 3: Quality Evaluation
|
|||
|
|
# - Integrated with Layer 4: Energy Allocation
|
|||
|
|
# Author : CorrectBench Integration
|
|||
|
|
# """
|
|||
|
|
# import os
|
|||
|
|
# import sys
|
|||
|
|
# import shutil
|
|||
|
|
|
|||
|
|
# import LLM_call as llm
|
|||
|
|
# import loader_saver as ls
|
|||
|
|
# from loader_saver import autologger as logger
|
|||
|
|
# from utils.verilator_call import verilator_run_coverage
|
|||
|
|
# from autoline.cga_utils import CoverageParser, TBInjector
|
|||
|
|
# # [新增] 导入语义分析层
|
|||
|
|
# from autoline.semantic_analyzer import SemanticAnalyzer, FunctionPointType
|
|||
|
|
# # [新增] 导入能量分配层
|
|||
|
|
# from autoline.energy_allocator import EnergyAllocator, EnergyState
|
|||
|
|
# # [新增] 导入多样性约束注入器
|
|||
|
|
# from autoline.diversity_injector import DiversityInjector
|
|||
|
|
# # [新增] 导入测试历史管理器
|
|||
|
|
# from autoline.test_history import TestHistoryManager
|
|||
|
|
# # [新增] 导入质量评估层
|
|||
|
|
# from autoline.quality_evaluator import QualityEvaluator, DiversityScore, SemanticCoverageResult
|
|||
|
|
|
|||
|
|
# class TaskTBCGA:
|
|||
|
|
# def __init__(self, task_dir, task_id, header, DUT_code, TB_code, config):
|
|||
|
|
# self.task_dir = task_dir
|
|||
|
|
# self.task_id = task_id
|
|||
|
|
# self.header = header
|
|||
|
|
# self.DUT_code = DUT_code
|
|||
|
|
# self.TB_code = TB_code
|
|||
|
|
# self.config = config
|
|||
|
|
|
|||
|
|
# self.max_iter = 5
|
|||
|
|
# self.target_coverage = 95.0
|
|||
|
|
# self.model = config.gpt.model
|
|||
|
|
|
|||
|
|
# self.best_tb = TB_code
|
|||
|
|
# self.best_score = 0.0
|
|||
|
|
|
|||
|
|
# # [新增] 能量分配器
|
|||
|
|
# self.energy_allocator: EnergyAllocator = None
|
|||
|
|
# # [新增] 多样性约束注入器
|
|||
|
|
# self.diversity_injector: DiversityInjector = None
|
|||
|
|
# # [新增] 质量评估器
|
|||
|
|
# self.quality_evaluator: QualityEvaluator = None
|
|||
|
|
|
|||
|
|
# # [新增辅助函数] 从父目录拷贝 DUT
|
|||
|
|
# def _prepare_dut(self, target_dir):
|
|||
|
|
# source_dut = os.path.join(self.task_dir, "DUT.v")
|
|||
|
|
# target_dut = os.path.join(target_dir, "DUT.v")
|
|||
|
|
|
|||
|
|
# # 优先拷贝现有的文件
|
|||
|
|
# if os.path.exists(source_dut):
|
|||
|
|
# shutil.copy(source_dut, target_dut)
|
|||
|
|
# else:
|
|||
|
|
# # 只有当文件由于某种原因被删除了,才降级使用内存中的 code
|
|||
|
|
# ls.save_code(self.DUT_code, target_dut)
|
|||
|
|
|
|||
|
|
# def _generate_exploration_prompt(self, iteration: int) -> str:
|
|||
|
|
# """
|
|||
|
|
# 生成探索性测试 Prompt
|
|||
|
|
|
|||
|
|
# 当找不到明确的 missing blocks 但覆盖率仍未达标时,
|
|||
|
|
# 生成一个探索性 Prompt 来尝试发现新的测试路径。
|
|||
|
|
|
|||
|
|
# Args:
|
|||
|
|
# iteration: 当前迭代次数
|
|||
|
|
|
|||
|
|
# Returns:
|
|||
|
|
# 探索性测试 Prompt,如果无法生成则返回 None
|
|||
|
|
# """
|
|||
|
|
# # 从语义分析结果获取 FSM 和功能点信息
|
|||
|
|
# fsm_info = ""
|
|||
|
|
# if self.semantic_result:
|
|||
|
|
# fsm_data = self.semantic_result.get('fsm', {})
|
|||
|
|
# if fsm_data:
|
|||
|
|
# states = fsm_data.get('states', [])
|
|||
|
|
# state_var = fsm_data.get('state_variable', 'state')
|
|||
|
|
# fsm_info = f"""
|
|||
|
|
# [FSM INFORMATION]
|
|||
|
|
# - State variable: {state_var}
|
|||
|
|
# - Known states: {', '.join(states) if states else 'unknown'}
|
|||
|
|
|
|||
|
|
# The DUT appears to be a Finite State Machine. To improve coverage:
|
|||
|
|
# 1. Try to visit each state by driving inputs that trigger state transitions
|
|||
|
|
# 2. For each state, try different input combinations
|
|||
|
|
# 3. Consider edge cases: reset transitions, timeout conditions, error states
|
|||
|
|
# """
|
|||
|
|
|
|||
|
|
# # 从能量分配器获取目标功能点
|
|||
|
|
# target_info = ""
|
|||
|
|
# if self.energy_allocator and self.energy_allocator.current_target:
|
|||
|
|
# target = self.energy_allocator.current_target
|
|||
|
|
# target_info = f"""
|
|||
|
|
# [CURRENT TARGET]
|
|||
|
|
# Focus on: {target.function_point}
|
|||
|
|
# Remaining energy: {target.remaining}
|
|||
|
|
# """
|
|||
|
|
|
|||
|
|
# # 从多样性注入器获取已尝试的测试
|
|||
|
|
# diversity_hints = ""
|
|||
|
|
# if self.diversity_injector:
|
|||
|
|
# history = self.diversity_injector.history
|
|||
|
|
# # if history and len(history.history) > 0:
|
|||
|
|
# # recent_tests = history.history[-5:] if len(history.history) > 5 else history.history
|
|||
|
|
# if history and hasattr(history, 'records') and len(history.records) > 0:
|
|||
|
|
# recent_tests = history.records[-5:] if len(history.records) > 5 else history.records
|
|||
|
|
# diversity_hints = f"""
|
|||
|
|
# [RECENTLY TRIED APPROACHES - AVOID REPETITION]
|
|||
|
|
# Recent test patterns tried:
|
|||
|
|
# """
|
|||
|
|
# # for i, test in enumerate(recent_tests):
|
|||
|
|
# # diversity_hints += f"- Iter {test.get('iteration', i)}: target={test.get('target_function', 'unknown')}\n"
|
|||
|
|
# for i, test in enumerate(recent_tests):
|
|||
|
|
# # TestRecord 是 dataclass,使用属性访问
|
|||
|
|
# target = getattr(test, 'target_function', 'unknown') if hasattr(test, 'target_function') else 'unknown'
|
|||
|
|
# iteration = getattr(test, 'iteration', i) if hasattr(test, 'iteration') else i
|
|||
|
|
# diversity_hints += f"- Iter {iteration}: target={target}\n"
|
|||
|
|
|
|||
|
|
# prompt = f"""
|
|||
|
|
# [EXPLORATION MODE - ITERATION {iteration}]
|
|||
|
|
|
|||
|
|
# Current coverage is {self.best_score:.2f}%, but no specific uncovered code blocks were identified.
|
|||
|
|
# This may happen when:
|
|||
|
|
# 1. Coverage data is incomplete or filtered
|
|||
|
|
# 2. Branch/condition coverage needs improvement (not just line coverage)
|
|||
|
|
# 3. State transitions in FSM are not fully exercised
|
|||
|
|
|
|||
|
|
# {fsm_info}
|
|||
|
|
# {target_info}
|
|||
|
|
# {diversity_hints}
|
|||
|
|
|
|||
|
|
# [YOUR TASK]
|
|||
|
|
# Write an EXPLORATORY test scenario that:
|
|||
|
|
# 1. Covers different input combinations than previous tests
|
|||
|
|
# 2. Explores different FSM state transitions
|
|||
|
|
# 3. Tests edge cases and boundary conditions
|
|||
|
|
# 4. Varies timing and sequence of inputs
|
|||
|
|
|
|||
|
|
# [OUTPUT FORMAT]
|
|||
|
|
# Return ONLY Verilog test scenario code (no task wrapper).
|
|||
|
|
# Use the signal names from the testbench.
|
|||
|
|
|
|||
|
|
# ```verilog
|
|||
|
|
# // Your exploratory test code here
|
|||
|
|
# ```
|
|||
|
|
# """
|
|||
|
|
# return prompt
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
# def run(self):
|
|||
|
|
# logger.info(f"[{self.task_id}] Starting Coverage-Guided Agent (CGA)...")
|
|||
|
|
|
|||
|
|
# # 1. 确保工作目录存在 (saves/任务名/5_CGA)
|
|||
|
|
# work_dir = os.path.join(self.task_dir, "5_CGA")
|
|||
|
|
# if os.path.exists(work_dir):
|
|||
|
|
# shutil.rmtree(work_dir)
|
|||
|
|
# os.makedirs(work_dir, exist_ok=True)
|
|||
|
|
|
|||
|
|
# # === [新增] Step 0: 语义分析 ===
|
|||
|
|
# logger.info(f"[{self.task_id}] Running Semantic Analysis (Layer 0)...")
|
|||
|
|
# self.semantic_result = None
|
|||
|
|
# try:
|
|||
|
|
# semantic_analyzer = SemanticAnalyzer(self.DUT_code)
|
|||
|
|
# self.semantic_result = semantic_analyzer.analyze()
|
|||
|
|
|
|||
|
|
# # 记录分析结果摘要
|
|||
|
|
# fp_count = len(self.semantic_result.get('function_points', []))
|
|||
|
|
# fsm_info = semantic_analyzer.get_fsm_info()
|
|||
|
|
# if fsm_info:
|
|||
|
|
# logger.info(f" FSM detected: {fsm_info.get('state_variable', 'unknown')} "
|
|||
|
|
# f"({len(fsm_info.get('states', []))} states)")
|
|||
|
|
# logger.info(f" Total function points identified: {fp_count}")
|
|||
|
|
|
|||
|
|
# # 保存语义分析报告
|
|||
|
|
# semantic_report = semantic_analyzer.generate_prompt_context()
|
|||
|
|
# ls.save_code(semantic_report, os.path.join(work_dir, "semantic_analysis.txt"))
|
|||
|
|
|
|||
|
|
# # === [新增] Step 0.1: 初始化能量分配器 ===
|
|||
|
|
# if self.semantic_result.get('function_points'):
|
|||
|
|
# self.energy_allocator = EnergyAllocator(max_iterations=self.max_iter)
|
|||
|
|
# energy_init_result = self.energy_allocator.initialize(
|
|||
|
|
# self.semantic_result['function_points']
|
|||
|
|
# )
|
|||
|
|
# logger.info(f" Energy allocator initialized: {energy_init_result['targets']} targets")
|
|||
|
|
|
|||
|
|
# # === [新增] Step 0.2: 初始化多样性约束注入器 ===
|
|||
|
|
# history_file = os.path.join(work_dir, "test_history.json")
|
|||
|
|
# # 创建 TestHistoryManager 并传递 history_file
|
|||
|
|
# history_manager = TestHistoryManager(history_file=history_file)
|
|||
|
|
# self.diversity_injector = DiversityInjector(history_manager=history_manager)
|
|||
|
|
# logger.info(f" Diversity injector initialized with history file: {history_file}")
|
|||
|
|
|
|||
|
|
# # === [新增] Step 0.3: 初始化质量评估器 ===
|
|||
|
|
# if self.semantic_result.get('function_points'):
|
|||
|
|
# self.quality_evaluator = QualityEvaluator(
|
|||
|
|
# function_points=self.semantic_result['function_points']
|
|||
|
|
# )
|
|||
|
|
# logger.info(f" Quality evaluator initialized")
|
|||
|
|
|
|||
|
|
# except Exception as e:
|
|||
|
|
# logger.warning(f"Semantic analysis failed: {e}. Continuing without semantic guidance.")
|
|||
|
|
# # ================================
|
|||
|
|
|
|||
|
|
# current_tb = self.TB_code
|
|||
|
|
# last_annotated_file = None
|
|||
|
|
|
|||
|
|
# # --- Baseline ---
|
|||
|
|
# logger.info(f"--- CGA Iter 0 (Baseline) ---")
|
|||
|
|
# iter0_dir = os.path.join(work_dir, "iter_0")
|
|||
|
|
# os.makedirs(iter0_dir, exist_ok=True)
|
|||
|
|
|
|||
|
|
# self._prepare_dut(iter0_dir)
|
|||
|
|
# ls.save_code(current_tb, os.path.join(iter0_dir, "driver.v"))
|
|||
|
|
|
|||
|
|
# success, score, annotated_path = verilator_run_coverage(iter0_dir, "DUT.v", "driver.v")
|
|||
|
|
|
|||
|
|
# self.best_score = score
|
|||
|
|
# self.best_tb = current_tb
|
|||
|
|
# last_annotated_file = annotated_path
|
|||
|
|
|
|||
|
|
# logger.info(f"Baseline Coverage: {score:.2f}%")
|
|||
|
|
|
|||
|
|
# if score >= self.target_coverage:
|
|||
|
|
# logger.success(f"Target reached at baseline!")
|
|||
|
|
# # [修改] 返回元组 (代码, 分数)
|
|||
|
|
# return self.best_tb, self.best_score
|
|||
|
|
|
|||
|
|
# # --- Loop ---
|
|||
|
|
# for i in range(1, self.max_iter + 1):
|
|||
|
|
# logger.info(f"--- CGA Iter {i} / {self.max_iter} ---")
|
|||
|
|
|
|||
|
|
# # === [新增] 能量检查:是否还有活跃目标 ===
|
|||
|
|
# if self.energy_allocator:
|
|||
|
|
# current_target = self.energy_allocator.select_next_target()
|
|||
|
|
# if not current_target:
|
|||
|
|
# logger.info("No more active targets with remaining energy. Stopping.")
|
|||
|
|
# break
|
|||
|
|
# logger.info(f"Target: {current_target}")
|
|||
|
|
# # =========================================
|
|||
|
|
|
|||
|
|
# if not last_annotated_file: break
|
|||
|
|
|
|||
|
|
# # [修改] 传递语义分析结果、能量分配器、多样性注入器给 CoverageParser
|
|||
|
|
# parser = CoverageParser(
|
|||
|
|
# last_annotated_file,
|
|||
|
|
# tb_code=self.best_tb,
|
|||
|
|
# semantic_result=self.semantic_result,
|
|||
|
|
# energy_allocator=self.energy_allocator,
|
|||
|
|
# diversity_injector=self.diversity_injector # [新增]
|
|||
|
|
# )
|
|||
|
|
# prompt = parser.generate_prompt(self.best_score)
|
|||
|
|
|
|||
|
|
# # if not prompt:
|
|||
|
|
# # logger.info("No reachable missing blocks found. Stopping.")
|
|||
|
|
# # break
|
|||
|
|
# if not prompt:
|
|||
|
|
# if self.best_score >= self.target_coverage:
|
|||
|
|
# break # 达标才停止
|
|||
|
|
# else:
|
|||
|
|
# # 未达标,尝试探索性测试
|
|||
|
|
# prompt = self._generate_exploration_prompt(i)
|
|||
|
|
|
|||
|
|
# logger.info(f"Asking LLM to fix missing logic (Current: {self.best_score:.2f}%)...")
|
|||
|
|
# messages = [{"role": "user", "content": prompt}]
|
|||
|
|
|
|||
|
|
# try:
|
|||
|
|
# response, _ = llm.llm_call(messages, self.model)
|
|||
|
|
# codes = llm.extract_code(response, "verilog")
|
|||
|
|
# new_task_code = codes[0] if codes else ""
|
|||
|
|
# if not new_task_code:
|
|||
|
|
# # [新增] 记录失败
|
|||
|
|
# if self.energy_allocator:
|
|||
|
|
# self.energy_allocator.record_generation(
|
|||
|
|
# success=False,
|
|||
|
|
# coverage_delta=0.0,
|
|||
|
|
# energy_cost=1.0
|
|||
|
|
# )
|
|||
|
|
# continue
|
|||
|
|
# except Exception as e:
|
|||
|
|
# logger.error(f"LLM Call failed: {e}")
|
|||
|
|
# # [新增] 记录失败
|
|||
|
|
# if self.energy_allocator:
|
|||
|
|
# self.energy_allocator.record_generation(
|
|||
|
|
# success=False,
|
|||
|
|
# coverage_delta=0.0,
|
|||
|
|
# energy_cost=1.0
|
|||
|
|
# )
|
|||
|
|
# break
|
|||
|
|
|
|||
|
|
# injector = TBInjector(self.best_tb)
|
|||
|
|
# enhanced_tb = injector.inject(new_task_code, iter_idx=i)
|
|||
|
|
|
|||
|
|
# iter_dir = os.path.join(work_dir, f"iter_{i}")
|
|||
|
|
# os.makedirs(iter_dir, exist_ok=True)
|
|||
|
|
|
|||
|
|
# self._prepare_dut(iter_dir)
|
|||
|
|
# ls.save_code(enhanced_tb, os.path.join(iter_dir, "driver.v"))
|
|||
|
|
|
|||
|
|
# success, new_score, new_annotated_path = verilator_run_coverage(iter_dir, "DUT.v", "driver.v")
|
|||
|
|
|
|||
|
|
# # === [新增] 记录生成结果到能量分配器 ===
|
|||
|
|
# coverage_delta = new_score - self.best_score if success else 0.0
|
|||
|
|
# generation_success = success and new_score > self.best_score
|
|||
|
|
|
|||
|
|
# if self.energy_allocator:
|
|||
|
|
# self.energy_allocator.record_generation(
|
|||
|
|
# success=generation_success,
|
|||
|
|
# coverage_delta=coverage_delta,
|
|||
|
|
# energy_cost=1.0
|
|||
|
|
# )
|
|||
|
|
# # =========================================
|
|||
|
|
|
|||
|
|
# # === [新增] 记录测试用例到多样性历史 ===
|
|||
|
|
# if self.diversity_injector:
|
|||
|
|
# # 提取已知信号
|
|||
|
|
# known_signals = []
|
|||
|
|
# if self.semantic_result:
|
|||
|
|
# known_signals = [p.get('name', '') for p in self.semantic_result.get('ports', [])]
|
|||
|
|
|
|||
|
|
# self.diversity_injector.record_test(
|
|||
|
|
# code=new_task_code,
|
|||
|
|
# target_function=self.energy_allocator.current_target.function_point if self.energy_allocator and self.energy_allocator.current_target else "",
|
|||
|
|
# coverage_score=new_score,
|
|||
|
|
# success=generation_success,
|
|||
|
|
# iteration=i,
|
|||
|
|
# known_signals=known_signals
|
|||
|
|
# )
|
|||
|
|
# # =======================================
|
|||
|
|
|
|||
|
|
# # === [新增] Layer 3: 质量评估 ===
|
|||
|
|
# if self.quality_evaluator:
|
|||
|
|
# # 评估测试用例质量
|
|||
|
|
# eval_result = self.quality_evaluator.evaluate_test_case(
|
|||
|
|
# code=new_task_code,
|
|||
|
|
# covered_lines=set(), # 如果有具体覆盖行信息可传入
|
|||
|
|
# covered_functions=[], # 如果有覆盖功能点信息可传入
|
|||
|
|
# test_id=f"iter_{i}",
|
|||
|
|
# iteration=i
|
|||
|
|
# )
|
|||
|
|
|
|||
|
|
# # 记录多样性得分
|
|||
|
|
# diversity_score = eval_result.get('diversity', {}).get('overall_score', 0)
|
|||
|
|
# logger.info(f" Quality Evaluation: diversity={diversity_score:.2f}")
|
|||
|
|
|
|||
|
|
# # 检查是否应该接受该测试用例
|
|||
|
|
# should_accept, reason = self.quality_evaluator.should_accept(eval_result)
|
|||
|
|
# if not should_accept:
|
|||
|
|
# logger.warning(f" Quality check failed: {reason}")
|
|||
|
|
# # =====================================
|
|||
|
|
|
|||
|
|
# if success and new_score > self.best_score:
|
|||
|
|
# improvement = new_score - self.best_score
|
|||
|
|
# logger.success(f"Coverage Improved! +{improvement:.2f}% ({self.best_score:.2f}% -> {new_score:.2f}%)")
|
|||
|
|
# self.best_score = new_score
|
|||
|
|
# self.best_tb = enhanced_tb
|
|||
|
|
# last_annotated_file = new_annotated_path
|
|||
|
|
# elif success and new_score == self.best_score:
|
|||
|
|
# logger.info(f"Coverage unchanged. Keeping previous.")
|
|||
|
|
# else:
|
|||
|
|
# logger.warning(f"Regression or Failure. Discarding changes.")
|
|||
|
|
|
|||
|
|
# if self.best_score >= self.target_coverage:
|
|||
|
|
# logger.success("Target coverage reached!")
|
|||
|
|
# break
|
|||
|
|
|
|||
|
|
# logger.info(f"CGA Finished. Final Coverage: {self.best_score:.2f}%")
|
|||
|
|
|
|||
|
|
# # === [新增] 生成能量分配报告 ===
|
|||
|
|
# if self.energy_allocator:
|
|||
|
|
# energy_report = self.energy_allocator.generate_report()
|
|||
|
|
# ls.save_code(energy_report, os.path.join(work_dir, "energy_report.txt"))
|
|||
|
|
# logger.info(f"Energy report saved to {work_dir}/energy_report.txt")
|
|||
|
|
# # =================================
|
|||
|
|
|
|||
|
|
# # === [新增] 生成多样性报告并保存历史 ===
|
|||
|
|
# if self.diversity_injector:
|
|||
|
|
# diversity_report = self.diversity_injector.generate_diversity_report()
|
|||
|
|
# ls.save_code(diversity_report, os.path.join(work_dir, "diversity_report.txt"))
|
|||
|
|
# logger.info(f"Diversity report saved to {work_dir}/diversity_report.txt")
|
|||
|
|
|
|||
|
|
# # 保存测试历史
|
|||
|
|
# self.diversity_injector.history.save()
|
|||
|
|
# # ======================================
|
|||
|
|
|
|||
|
|
# # === [新增] Layer 3: 生成质量评估报告 ===
|
|||
|
|
# if self.quality_evaluator:
|
|||
|
|
# quality_report = self.quality_evaluator.generate_report()
|
|||
|
|
# ls.save_code(quality_report, os.path.join(work_dir, "quality_evaluation_report.txt"))
|
|||
|
|
# logger.info(f"Quality evaluation report saved to {work_dir}/quality_evaluation_report.txt")
|
|||
|
|
|
|||
|
|
# # 输出语义覆盖率摘要
|
|||
|
|
# coverage_result = self.quality_evaluator.semantic_coverage.calculate_coverage()
|
|||
|
|
# logger.info(f"Semantic Coverage: {coverage_result.semantic_coverage:.2%}")
|
|||
|
|
# # ===========================================
|
|||
|
|
|
|||
|
|
# # [修改] 返回元组 (代码, 分数)
|
|||
|
|
# return self.best_tb, self.best_score
|
|||
|
|
|
|||
|
|
"""
|
|||
|
|
Description : Coverage-Guided Agent (CGA) Main Controller
|
|||
|
|
- Integrated with Layer 0: Semantic Analysis
|
|||
|
|
- Integrated with Layer 1: Diversity Constraint Injection
|
|||
|
|
- Integrated with Layer 3: Quality Evaluation
|
|||
|
|
- Integrated with Layer 4: Energy Allocation
|
|||
|
|
Author : CorrectBench Integration
|
|||
|
|
"""
|
|||
|
|
import os
|
|||
|
|
import re
|
|||
|
|
import sys
|
|||
|
|
import shutil
|
|||
|
|
|
|||
|
|
import LLM_call as llm
|
|||
|
|
import loader_saver as ls
|
|||
|
|
from loader_saver import autologger as logger
|
|||
|
|
from utils.verilator_call import verilator_run_coverage
|
|||
|
|
from autoline.cga_utils import CoverageParser, TBInjector
|
|||
|
|
# [新增] 导入语义分析层
|
|||
|
|
from autoline.semantic_analyzer import SemanticAnalyzer, FunctionPointType
|
|||
|
|
# [新增] 导入能量分配层
|
|||
|
|
from autoline.energy_allocator import EnergyAllocator, EnergyState
|
|||
|
|
# [新增] 导入多样性约束注入器
|
|||
|
|
from autoline.diversity_injector import DiversityInjector
|
|||
|
|
# [新增] 导入测试历史管理器
|
|||
|
|
from autoline.test_history import TestHistoryManager
|
|||
|
|
# [新增] 导入质量评估层
|
|||
|
|
from autoline.quality_evaluator import QualityEvaluator, DiversityScore, SemanticCoverageResult
|
|||
|
|
|
|||
|
|
class TaskTBCGA:
|
|||
|
|
def __init__(self, task_dir, task_id, header, DUT_code, TB_code, config, work_subdir="CGA", max_iter=None):
|
|||
|
|
self.task_dir = task_dir
|
|||
|
|
self.task_id = task_id
|
|||
|
|
self.header = header
|
|||
|
|
self.DUT_code = DUT_code
|
|||
|
|
self.TB_code = TB_code
|
|||
|
|
self.config = config
|
|||
|
|
self.work_subdir = work_subdir
|
|||
|
|
|
|||
|
|
self.max_iter = config.autoline.cga.max_iter if max_iter is None else max_iter
|
|||
|
|
self.target_coverage = config.autoline.cga.target_coverage
|
|||
|
|
self.model = config.gpt.model
|
|||
|
|
|
|||
|
|
self.best_tb = TB_code
|
|||
|
|
self.best_score = 0.0
|
|||
|
|
self.best_covered_lines = set()
|
|||
|
|
self.best_covered_functions = set()
|
|||
|
|
|
|||
|
|
# [新增] 能量分配器
|
|||
|
|
self.energy_allocator: EnergyAllocator = None
|
|||
|
|
# [新增] 多样性约束注入器
|
|||
|
|
self.diversity_injector: DiversityInjector = None
|
|||
|
|
# [新增] 质量评估器
|
|||
|
|
self.quality_evaluator: QualityEvaluator = None
|
|||
|
|
|
|||
|
|
# [新增辅助函数] 从父目录拷贝 DUT
|
|||
|
|
def _prepare_dut(self, target_dir):
|
|||
|
|
source_dut = os.path.join(self.task_dir, "DUT.v")
|
|||
|
|
target_dut = os.path.join(target_dir, "DUT.v")
|
|||
|
|
|
|||
|
|
# 优先拷贝现有的文件
|
|||
|
|
if os.path.exists(source_dut):
|
|||
|
|
shutil.copy(source_dut, target_dut)
|
|||
|
|
else:
|
|||
|
|
# 只有当文件由于某种原因被删除了,才降级使用内存中的 code
|
|||
|
|
ls.save_code(self.DUT_code, target_dut)
|
|||
|
|
|
|||
|
|
def _extract_coverage_snapshot(self, annotated_path):
|
|||
|
|
"""
|
|||
|
|
从 Verilator annotated DUT 中提取当前已覆盖行和已覆盖功能点。
|
|||
|
|
"""
|
|||
|
|
snapshot = {
|
|||
|
|
"covered_lines": set(),
|
|||
|
|
"covered_functions": set(),
|
|||
|
|
"coverable_lines": set(),
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if not annotated_path or not os.path.exists(annotated_path):
|
|||
|
|
return snapshot
|
|||
|
|
|
|||
|
|
pct_pattern = re.compile(r"^%(\d+)\s+(.*)$")
|
|||
|
|
tilde_pattern = re.compile(r"^~(\d+)\s+(.*)$")
|
|||
|
|
caret_pattern = re.compile(r"^\^(\d+)\s+(.*)$")
|
|||
|
|
plain_pattern = re.compile(r"^\s*(\d+)\s+(.*)$")
|
|||
|
|
decl_pattern = re.compile(r"^\s*(input|output|inout|wire|reg|logic|parameter|localparam|assign)\b")
|
|||
|
|
|
|||
|
|
with open(annotated_path, "r", encoding="utf-8", errors="ignore") as f:
|
|||
|
|
for line_no, raw_line in enumerate(f, start=1):
|
|||
|
|
stripped = raw_line.strip()
|
|||
|
|
if not stripped:
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
count = None
|
|||
|
|
code_part = None
|
|||
|
|
is_caret = False
|
|||
|
|
|
|||
|
|
match = pct_pattern.match(stripped)
|
|||
|
|
if match:
|
|||
|
|
count = int(match.group(1))
|
|||
|
|
code_part = match.group(2).strip()
|
|||
|
|
else:
|
|||
|
|
match = tilde_pattern.match(stripped)
|
|||
|
|
if match:
|
|||
|
|
count = int(match.group(1))
|
|||
|
|
code_part = match.group(2).strip()
|
|||
|
|
else:
|
|||
|
|
match = caret_pattern.match(stripped)
|
|||
|
|
if match:
|
|||
|
|
is_caret = True
|
|||
|
|
code_part = match.group(2).strip()
|
|||
|
|
else:
|
|||
|
|
match = plain_pattern.match(stripped)
|
|||
|
|
if match:
|
|||
|
|
count = int(match.group(1))
|
|||
|
|
code_part = match.group(2).strip()
|
|||
|
|
|
|||
|
|
if code_part is None:
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
if "//" in code_part:
|
|||
|
|
code_part = code_part.split("//", 1)[0].strip()
|
|||
|
|
|
|||
|
|
if not code_part:
|
|||
|
|
continue
|
|||
|
|
if decl_pattern.match(code_part):
|
|||
|
|
continue
|
|||
|
|
if code_part in {"begin", "end", "else", "endmodule", "endcase", ");", "default:"}:
|
|||
|
|
continue
|
|||
|
|
if not any(ch.isalnum() for ch in code_part):
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
snapshot["coverable_lines"].add(line_no)
|
|||
|
|
if (count is not None) and (count > 0) and not is_caret:
|
|||
|
|
snapshot["covered_lines"].add(line_no)
|
|||
|
|
|
|||
|
|
snapshot["covered_functions"] = self._map_lines_to_function_points(snapshot["covered_lines"])
|
|||
|
|
return snapshot
|
|||
|
|
|
|||
|
|
def _map_lines_to_function_points(self, covered_lines):
|
|||
|
|
"""
|
|||
|
|
用功能点 location 与已覆盖行做交集,推断当前已命中的功能点。
|
|||
|
|
"""
|
|||
|
|
matched = set()
|
|||
|
|
|
|||
|
|
if not self.semantic_result:
|
|||
|
|
return matched
|
|||
|
|
|
|||
|
|
for fp in self.semantic_result.get("function_points", []):
|
|||
|
|
location = fp.get("location", {})
|
|||
|
|
start_line = location.get("start_line", 0)
|
|||
|
|
end_line = location.get("end_line", 0)
|
|||
|
|
if (start_line <= 0) or (end_line <= 0):
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
if any(start_line <= line_no <= end_line for line_no in covered_lines):
|
|||
|
|
matched.add(fp.get("name", ""))
|
|||
|
|
|
|||
|
|
matched.discard("")
|
|||
|
|
return matched
|
|||
|
|
|
|||
|
|
def _generate_exploration_prompt(self, iteration: int) -> str:
|
|||
|
|
"""
|
|||
|
|
生成探索性测试 Prompt
|
|||
|
|
|
|||
|
|
当找不到明确的 missing blocks 但覆盖率仍未达标时,
|
|||
|
|
生成一个探索性 Prompt 来尝试发现新的测试路径。
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
iteration: 当前迭代次数
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
探索性测试 Prompt,如果无法生成则返回 None
|
|||
|
|
"""
|
|||
|
|
# 从语义分析结果获取 FSM 和功能点信息
|
|||
|
|
fsm_info = ""
|
|||
|
|
if self.semantic_result:
|
|||
|
|
fsm_data = self.semantic_result.get('fsm', {})
|
|||
|
|
if fsm_data:
|
|||
|
|
states = fsm_data.get('states', [])
|
|||
|
|
state_var = fsm_data.get('state_variable', 'state')
|
|||
|
|
fsm_info = f"""
|
|||
|
|
[FSM INFORMATION]
|
|||
|
|
- State variable: {state_var}
|
|||
|
|
- Known states: {', '.join(states) if states else 'unknown'}
|
|||
|
|
|
|||
|
|
The DUT appears to be a Finite State Machine. To improve coverage:
|
|||
|
|
1. Try to visit each state by driving inputs that trigger state transitions
|
|||
|
|
2. For each state, try different input combinations
|
|||
|
|
3. Consider edge cases: reset transitions, timeout conditions, error states
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
# 从能量分配器获取目标功能点
|
|||
|
|
target_info = ""
|
|||
|
|
if self.energy_allocator and self.energy_allocator.current_target:
|
|||
|
|
target = self.energy_allocator.current_target
|
|||
|
|
target_info = f"""
|
|||
|
|
[CURRENT TARGET]
|
|||
|
|
Focus on: {target.function_point}
|
|||
|
|
Remaining energy: {target.remaining}
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
# 从多样性注入器获取已尝试的测试
|
|||
|
|
diversity_hints = ""
|
|||
|
|
if self.diversity_injector:
|
|||
|
|
history = self.diversity_injector.history
|
|||
|
|
# [修复] TestHistoryManager 使用 records 属性,不是 history
|
|||
|
|
if history and hasattr(history, 'records') and len(history.records) > 0:
|
|||
|
|
recent_tests = history.records[-5:] if len(history.records) > 5 else history.records
|
|||
|
|
diversity_hints = f"""
|
|||
|
|
[RECENTLY TRIED APPROACHES - AVOID REPETITION]
|
|||
|
|
Recent test patterns tried:
|
|||
|
|
"""
|
|||
|
|
for i, test in enumerate(recent_tests):
|
|||
|
|
# TestRecord 是 dataclass,使用属性访问
|
|||
|
|
target = getattr(test, 'target_function', 'unknown') if hasattr(test, 'target_function') else test.get('target_function', 'unknown') if isinstance(test, dict) else 'unknown'
|
|||
|
|
iteration = getattr(test, 'iteration', i) if hasattr(test, 'iteration') else test.get('iteration', i) if isinstance(test, dict) else i
|
|||
|
|
diversity_hints += f"- Iter {iteration}: target={target}\n"
|
|||
|
|
|
|||
|
|
prompt = f"""
|
|||
|
|
[EXPLORATION MODE - ITERATION {iteration}]
|
|||
|
|
|
|||
|
|
Current coverage is {self.best_score:.2f}%, but no specific uncovered code blocks were identified.
|
|||
|
|
This may happen when:
|
|||
|
|
1. Coverage data is incomplete or filtered
|
|||
|
|
2. Branch/condition coverage needs improvement (not just line coverage)
|
|||
|
|
3. State transitions in FSM are not fully exercised
|
|||
|
|
|
|||
|
|
{fsm_info}
|
|||
|
|
{target_info}
|
|||
|
|
{diversity_hints}
|
|||
|
|
|
|||
|
|
[YOUR TASK]
|
|||
|
|
Write an EXPLORATORY test scenario that:
|
|||
|
|
1. Covers different input combinations than previous tests
|
|||
|
|
2. Explores different FSM state transitions
|
|||
|
|
3. Tests edge cases and boundary conditions
|
|||
|
|
4. Varies timing and sequence of inputs
|
|||
|
|
|
|||
|
|
[OUTPUT FORMAT]
|
|||
|
|
Return ONLY Verilog test scenario code (no task wrapper).
|
|||
|
|
Use the signal names from the testbench.
|
|||
|
|
|
|||
|
|
```verilog
|
|||
|
|
// Your exploratory test code here
|
|||
|
|
```
|
|||
|
|
"""
|
|||
|
|
return prompt
|
|||
|
|
|
|||
|
|
def _generate_syntax_fix_prompt(self, original_code: str, syntax_issues: dict, original_prompt: str) -> str:
|
|||
|
|
"""
|
|||
|
|
生成语法修正 Prompt,让 LLM 修复检测到的语法问题
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
original_code: 原始生成的代码
|
|||
|
|
syntax_issues: 语法检查结果
|
|||
|
|
original_prompt: 原始 Prompt
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
修正 Prompt
|
|||
|
|
"""
|
|||
|
|
issues_text = []
|
|||
|
|
|
|||
|
|
for issue in syntax_issues.get('width_mismatch', []):
|
|||
|
|
issues_text.append(f"- {issue['message']}")
|
|||
|
|
if 'suggestion' in issue:
|
|||
|
|
issues_text.append(f" Suggestion: {issue['suggestion']}")
|
|||
|
|
|
|||
|
|
for issue in syntax_issues.get('logic_issues', []):
|
|||
|
|
issues_text.append(f"- {issue['message']}")
|
|||
|
|
if 'suggestion' in issue:
|
|||
|
|
issues_text.append(f" Suggestion: {issue['suggestion']}")
|
|||
|
|
|
|||
|
|
for issue in syntax_issues.get('syntax_warnings', []):
|
|||
|
|
if issue['severity'] == 'error':
|
|||
|
|
issues_text.append(f"- ERROR: {issue['message']}")
|
|||
|
|
|
|||
|
|
prompt = f"""
|
|||
|
|
[SYNTAX FIX REQUEST]
|
|||
|
|
|
|||
|
|
The previously generated Verilog test code has the following issues:
|
|||
|
|
|
|||
|
|
{chr(10).join(issues_text)}
|
|||
|
|
|
|||
|
|
[ORIGINAL CODE]
|
|||
|
|
```verilog
|
|||
|
|
{original_code}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
[YOUR TASK]
|
|||
|
|
Fix the above code to address these issues. Pay special attention to:
|
|||
|
|
|
|||
|
|
1. **Width Mismatch**: When you want to input a bit sequence (e.g., 01111100) to a single-bit signal:
|
|||
|
|
- WRONG: `{{in}} = 8'b01111100;` (truncates to single bit)
|
|||
|
|
- CORRECT: Use a shift register
|
|||
|
|
```verilog
|
|||
|
|
reg [7:0] shift_reg;
|
|||
|
|
shift_reg = 8'b01111100;
|
|||
|
|
for (i = 0; i < 8; i = i + 1) begin
|
|||
|
|
in = shift_reg[7];
|
|||
|
|
shift_reg = shift_reg << 1;
|
|||
|
|
@(posedge clk);
|
|||
|
|
end
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
2. **Single-bit Shift**: Shifting a 1-bit signal has no effect:
|
|||
|
|
- WRONG: `in = in >> 1;` (always results in 0)
|
|||
|
|
- CORRECT: Use a multi-bit shift register as shown above
|
|||
|
|
|
|||
|
|
[OUTPUT FORMAT]
|
|||
|
|
Return ONLY the corrected Verilog test scenario code:
|
|||
|
|
```verilog
|
|||
|
|
// Your corrected test code here
|
|||
|
|
```
|
|||
|
|
"""
|
|||
|
|
return prompt
|
|||
|
|
|
|||
|
|
def _get_compile_error(self, iter_dir: str) -> str:
|
|||
|
|
"""
|
|||
|
|
获取 Verilator 编译错误日志
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
iter_dir: 迭代目录
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
错误日志字符串
|
|||
|
|
"""
|
|||
|
|
error_parts = []
|
|||
|
|
|
|||
|
|
# 检查 obj_dir 是否存在
|
|||
|
|
obj_dir = os.path.join(iter_dir, "obj_dir")
|
|||
|
|
if not os.path.exists(obj_dir):
|
|||
|
|
error_parts.append("obj_dir not created - compilation failed early")
|
|||
|
|
|
|||
|
|
# 检查可能的日志文件
|
|||
|
|
log_files = [
|
|||
|
|
os.path.join(iter_dir, "verilator.log"),
|
|||
|
|
os.path.join(iter_dir, "compile.log"),
|
|||
|
|
os.path.join(obj_dir, "Vtestbench.log"),
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
for log_file in log_files:
|
|||
|
|
if os.path.exists(log_file):
|
|||
|
|
try:
|
|||
|
|
with open(log_file, 'r', errors='ignore') as f:
|
|||
|
|
content = f.read()
|
|||
|
|
if content.strip():
|
|||
|
|
error_parts.append(f"=== {os.path.basename(log_file)} ===")
|
|||
|
|
error_parts.append(content[-2000:]) # 最后 2000 字符
|
|||
|
|
except Exception:
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
# 如果没有找到日志文件,检查目录内容
|
|||
|
|
if not error_parts:
|
|||
|
|
error_parts.append(f"Directory contents of {iter_dir}:")
|
|||
|
|
try:
|
|||
|
|
for item in os.listdir(iter_dir):
|
|||
|
|
error_parts.append(f" {item}")
|
|||
|
|
except Exception:
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
return '\n'.join(error_parts) if error_parts else "Unknown compilation error"
|
|||
|
|
|
|||
|
|
def _generate_compile_fix_prompt(self, compile_error: str, original_code: str) -> str:
|
|||
|
|
"""
|
|||
|
|
生成编译错误修正 Prompt
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
compile_error: 编译错误日志
|
|||
|
|
original_code: 原始代码
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
修正 Prompt
|
|||
|
|
"""
|
|||
|
|
# 截取关键错误信息
|
|||
|
|
error_lines = compile_error.split('\n')
|
|||
|
|
key_errors = []
|
|||
|
|
for line in error_lines:
|
|||
|
|
line = line.strip()
|
|||
|
|
if any(kw in line.lower() for kw in ['error', 'syntax', 'fatal', 'undefined', 'illegal']):
|
|||
|
|
key_errors.append(line)
|
|||
|
|
if len(key_errors) > 10: # 最多 10 条关键错误
|
|||
|
|
break
|
|||
|
|
|
|||
|
|
prompt = f"""
|
|||
|
|
[COMPILATION ERROR FIX REQUEST]
|
|||
|
|
|
|||
|
|
The Verilog test code failed to compile with Verilator. Here are the key errors:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
{chr(10).join(key_errors) if key_errors else compile_error[:1000]}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
[ORIGINAL CODE]
|
|||
|
|
```verilog
|
|||
|
|
{original_code[:2000]} // Truncated if too long
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
[COMMON VERILOG ISSUES TO CHECK]
|
|||
|
|
|
|||
|
|
1. **Width mismatch**: Assigning wide values to narrow signals
|
|||
|
|
- Problem: `{{in}} = 8'b01111100;` where `in` is 1-bit
|
|||
|
|
- Fix: Use shift register to input bits one at a time
|
|||
|
|
|
|||
|
|
2. **Undefined signals**: Using signals that are not declared
|
|||
|
|
- Check spelling of signal names against the testbench
|
|||
|
|
|
|||
|
|
3. **Syntax errors**: Missing semicolons, mismatched begin/end
|
|||
|
|
- Check all statements end with semicolon
|
|||
|
|
- Ensure all `begin` have matching `end`
|
|||
|
|
|
|||
|
|
4. **Timescale issues**: Missing timescale directive
|
|||
|
|
- The testbench should have `timescale 1ns / 1ps`
|
|||
|
|
|
|||
|
|
[YOUR TASK]
|
|||
|
|
Generate a CORRECTED version of the test code that will compile successfully.
|
|||
|
|
Focus on fixing the specific errors shown above.
|
|||
|
|
|
|||
|
|
[OUTPUT FORMAT]
|
|||
|
|
Return ONLY the corrected Verilog test scenario code:
|
|||
|
|
```verilog
|
|||
|
|
// Your corrected test code here
|
|||
|
|
```
|
|||
|
|
"""
|
|||
|
|
return prompt
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
def run(self):
|
|||
|
|
logger.info(f"[{self.task_id}] Starting Coverage-Guided Agent (CGA)...")
|
|||
|
|
|
|||
|
|
# 1. 确保工作目录存在 (saves/任务名/5_CGA)
|
|||
|
|
work_dir = os.path.join(self.task_dir, self.work_subdir)
|
|||
|
|
if os.path.exists(work_dir):
|
|||
|
|
shutil.rmtree(work_dir)
|
|||
|
|
os.makedirs(work_dir, exist_ok=True)
|
|||
|
|
|
|||
|
|
# === [新增] Step 0: 语义分析 ===
|
|||
|
|
logger.info(f"[{self.task_id}] Running Semantic Analysis (Layer 0)...")
|
|||
|
|
self.semantic_result = None
|
|||
|
|
try:
|
|||
|
|
semantic_analyzer = SemanticAnalyzer(self.DUT_code)
|
|||
|
|
self.semantic_result = semantic_analyzer.analyze()
|
|||
|
|
|
|||
|
|
# 记录分析结果摘要
|
|||
|
|
fp_count = len(self.semantic_result.get('function_points', []))
|
|||
|
|
fsm_info = semantic_analyzer.get_fsm_info()
|
|||
|
|
if fsm_info:
|
|||
|
|
logger.info(f" FSM detected: {fsm_info.get('state_variable', 'unknown')} "
|
|||
|
|
f"({len(fsm_info.get('states', []))} states)")
|
|||
|
|
logger.info(f" Total function points identified: {fp_count}")
|
|||
|
|
|
|||
|
|
# 保存语义分析报告
|
|||
|
|
semantic_report = semantic_analyzer.generate_prompt_context()
|
|||
|
|
ls.save_code(semantic_report, os.path.join(work_dir, "semantic_analysis.txt"))
|
|||
|
|
|
|||
|
|
# === [新增] Step 0.1: 初始化能量分配器 ===
|
|||
|
|
if self.semantic_result.get('function_points'):
|
|||
|
|
self.energy_allocator = EnergyAllocator(max_iterations=self.max_iter)
|
|||
|
|
energy_init_result = self.energy_allocator.initialize(
|
|||
|
|
self.semantic_result['function_points']
|
|||
|
|
)
|
|||
|
|
logger.info(f" Energy allocator initialized: {energy_init_result['targets']} targets")
|
|||
|
|
|
|||
|
|
# === [新增] Step 0.2: 初始化多样性约束注入器 ===
|
|||
|
|
history_file = os.path.join(work_dir, "test_history.json")
|
|||
|
|
# 创建 TestHistoryManager 并传递 history_file
|
|||
|
|
history_manager = TestHistoryManager(history_file=history_file)
|
|||
|
|
self.diversity_injector = DiversityInjector(history_manager=history_manager)
|
|||
|
|
logger.info(f" Diversity injector initialized with history file: {history_file}")
|
|||
|
|
|
|||
|
|
# === [新增] Step 0.3: 初始化质量评估器 ===
|
|||
|
|
if self.semantic_result.get('function_points'):
|
|||
|
|
self.quality_evaluator = QualityEvaluator(
|
|||
|
|
function_points=self.semantic_result['function_points']
|
|||
|
|
)
|
|||
|
|
logger.info(f" Quality evaluator initialized")
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.warning(f"Semantic analysis failed: {e}. Continuing without semantic guidance.")
|
|||
|
|
# ================================
|
|||
|
|
|
|||
|
|
current_tb = self.TB_code
|
|||
|
|
last_annotated_file = None
|
|||
|
|
|
|||
|
|
# --- Baseline ---
|
|||
|
|
logger.info(f"--- CGA Iter 0 (Baseline) ---")
|
|||
|
|
iter0_dir = os.path.join(work_dir, "iter_0")
|
|||
|
|
os.makedirs(iter0_dir, exist_ok=True)
|
|||
|
|
|
|||
|
|
self._prepare_dut(iter0_dir)
|
|||
|
|
ls.save_code(current_tb, os.path.join(iter0_dir, "driver.v"))
|
|||
|
|
|
|||
|
|
success, score, annotated_path = verilator_run_coverage(iter0_dir, "DUT.v", "driver.v")
|
|||
|
|
|
|||
|
|
self.best_score = score
|
|||
|
|
self.best_tb = current_tb
|
|||
|
|
last_annotated_file = annotated_path
|
|||
|
|
baseline_snapshot = self._extract_coverage_snapshot(annotated_path)
|
|||
|
|
self.best_covered_lines = set(baseline_snapshot["covered_lines"])
|
|||
|
|
self.best_covered_functions = set(baseline_snapshot["covered_functions"])
|
|||
|
|
|
|||
|
|
if self.energy_allocator and self.best_covered_functions:
|
|||
|
|
self.energy_allocator.mark_targets_completed(sorted(self.best_covered_functions))
|
|||
|
|
|
|||
|
|
if self.quality_evaluator and self.best_covered_functions:
|
|||
|
|
self.quality_evaluator.semantic_coverage.update_coverage(
|
|||
|
|
covered_lines=self.best_covered_lines,
|
|||
|
|
covered_functions=sorted(self.best_covered_functions),
|
|||
|
|
test_id="iter_0",
|
|||
|
|
iteration=0
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
logger.info(f"Baseline Coverage: {score:.2f}%")
|
|||
|
|
|
|||
|
|
if score >= self.target_coverage:
|
|||
|
|
logger.success(f"Target reached at baseline!")
|
|||
|
|
# [修改] 返回元组 (代码, 分数)
|
|||
|
|
return self.best_tb, self.best_score
|
|||
|
|
|
|||
|
|
# --- Loop ---
|
|||
|
|
for i in range(1, self.max_iter + 1):
|
|||
|
|
logger.info(f"--- CGA Iter {i} / {self.max_iter} ---")
|
|||
|
|
iter_dir = os.path.join(work_dir, f"iter_{i}")
|
|||
|
|
os.makedirs(iter_dir, exist_ok=True)
|
|||
|
|
|
|||
|
|
# === [新增] 能量检查:是否还有活跃目标 ===
|
|||
|
|
if self.energy_allocator:
|
|||
|
|
current_target = self.energy_allocator.select_next_target()
|
|||
|
|
if not current_target:
|
|||
|
|
logger.info("No more active targets with remaining energy. Stopping.")
|
|||
|
|
break
|
|||
|
|
logger.info(f"Target: {current_target}")
|
|||
|
|
# =========================================
|
|||
|
|
|
|||
|
|
if not last_annotated_file: break
|
|||
|
|
|
|||
|
|
# [修改] 传递语义分析结果、能量分配器、多样性注入器、DUT代码给 CoverageParser
|
|||
|
|
parser = CoverageParser(
|
|||
|
|
last_annotated_file,
|
|||
|
|
tb_code=self.best_tb,
|
|||
|
|
semantic_result=self.semantic_result,
|
|||
|
|
energy_allocator=self.energy_allocator,
|
|||
|
|
diversity_injector=self.diversity_injector, # [新增]
|
|||
|
|
dut_code=self.DUT_code # [新增] 传递 DUT 代码以提取信号名
|
|||
|
|
)
|
|||
|
|
prompt = parser.generate_prompt(self.best_score)
|
|||
|
|
|
|||
|
|
# [修改] 改进停止条件:即使找不到 missing_blocks,只要覆盖率未达标就继续
|
|||
|
|
if not prompt:
|
|||
|
|
if self.best_score >= self.target_coverage:
|
|||
|
|
logger.success(f"Target coverage reached: {self.best_score:.2f}%")
|
|||
|
|
break
|
|||
|
|
else:
|
|||
|
|
# 覆盖率未达标但找不到明确的 missing_blocks
|
|||
|
|
# 尝试生成随机探索 Prompt
|
|||
|
|
logger.warning(f"No reachable missing blocks found, but coverage ({self.best_score:.2f}%) < target ({self.target_coverage}%).")
|
|||
|
|
logger.info(f"Attempting random exploration to discover uncovered paths...")
|
|||
|
|
prompt = self._generate_exploration_prompt(i)
|
|||
|
|
|
|||
|
|
if not prompt:
|
|||
|
|
logger.info("Could not generate exploration prompt. Stopping.")
|
|||
|
|
break
|
|||
|
|
|
|||
|
|
ls.save_code(prompt, os.path.join(iter_dir, "prompt.txt"))
|
|||
|
|
logger.info(f"Asking LLM to fix missing logic (Current: {self.best_score:.2f}%)...")
|
|||
|
|
messages = [{"role": "user", "content": prompt}]
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
response, _ = llm.llm_call(messages, self.model)
|
|||
|
|
ls.save_code(response, os.path.join(iter_dir, "llm_response.txt"))
|
|||
|
|
codes = llm.extract_code(response, "verilog")
|
|||
|
|
new_task_code = codes[0] if codes else ""
|
|||
|
|
if not new_task_code:
|
|||
|
|
# [新增] 记录失败
|
|||
|
|
if self.energy_allocator:
|
|||
|
|
self.energy_allocator.record_generation(
|
|||
|
|
success=False,
|
|||
|
|
coverage_delta=0.0,
|
|||
|
|
energy_cost=1.0
|
|||
|
|
)
|
|||
|
|
continue
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error(f"LLM Call failed: {e}")
|
|||
|
|
# [新增] 记录失败
|
|||
|
|
if self.energy_allocator:
|
|||
|
|
self.energy_allocator.record_generation(
|
|||
|
|
success=False,
|
|||
|
|
coverage_delta=0.0,
|
|||
|
|
energy_cost=1.0
|
|||
|
|
)
|
|||
|
|
break
|
|||
|
|
|
|||
|
|
ls.save_code(new_task_code, os.path.join(iter_dir, "generated_scenario.v"))
|
|||
|
|
injector = TBInjector(self.best_tb)
|
|||
|
|
enhanced_tb = injector.inject(new_task_code, iter_idx=i)
|
|||
|
|
|
|||
|
|
# [新增] 检查语法预检查结果
|
|||
|
|
validation_result = injector.last_validation_result
|
|||
|
|
syntax_issues = validation_result.get('syntax_check', {}) if validation_result else {}
|
|||
|
|
|
|||
|
|
if syntax_issues.get('should_retry', False):
|
|||
|
|
logger.warning(f"[CGA-{i}] Syntax issues detected in generated code. Attempting retry...")
|
|||
|
|
# 生成修正后的 Prompt,包含语法问题提示
|
|||
|
|
retry_prompt = self._generate_syntax_fix_prompt(new_task_code, syntax_issues, prompt)
|
|||
|
|
if retry_prompt:
|
|||
|
|
try:
|
|||
|
|
retry_response, _ = llm.llm_call([{"role": "user", "content": retry_prompt}], self.model)
|
|||
|
|
retry_codes = llm.extract_code(retry_response, "verilog")
|
|||
|
|
if retry_codes:
|
|||
|
|
new_task_code = retry_codes[0]
|
|||
|
|
ls.save_code(retry_prompt, os.path.join(iter_dir, "retry_prompt.txt"))
|
|||
|
|
ls.save_code(retry_response, os.path.join(iter_dir, "retry_response.txt"))
|
|||
|
|
ls.save_code(new_task_code, os.path.join(iter_dir, "generated_scenario_retry.v"))
|
|||
|
|
enhanced_tb = injector.inject(new_task_code, iter_idx=i)
|
|||
|
|
logger.info(f"[CGA-{i}] Retry code generated successfully")
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.warning(f"[CGA-{i}] Retry failed: {e}")
|
|||
|
|
|
|||
|
|
self._prepare_dut(iter_dir)
|
|||
|
|
ls.save_code(enhanced_tb, os.path.join(iter_dir, "driver.v"))
|
|||
|
|
|
|||
|
|
success, new_score, new_annotated_path = verilator_run_coverage(iter_dir, "DUT.v", "driver.v")
|
|||
|
|
|
|||
|
|
# [新增] 编译失败时的错误反馈机制
|
|||
|
|
if not success:
|
|||
|
|
compile_error = self._get_compile_error(iter_dir)
|
|||
|
|
if compile_error:
|
|||
|
|
logger.error(f"[CGA-{i}] Verilator compilation failed:")
|
|||
|
|
logger.error(compile_error[:500]) # 截断过长的错误信息
|
|||
|
|
|
|||
|
|
# 尝试让 LLM 修正编译错误(最多 1 次重试)
|
|||
|
|
if not hasattr(self, '_compile_retry_count'):
|
|||
|
|
self._compile_retry_count = {}
|
|||
|
|
self._compile_retry_count[i] = self._compile_retry_count.get(i, 0)
|
|||
|
|
|
|||
|
|
if self._compile_retry_count[i] < 1:
|
|||
|
|
logger.info(f"[CGA-{i}] Asking LLM to fix compilation errors...")
|
|||
|
|
fix_prompt = self._generate_compile_fix_prompt(compile_error, new_task_code)
|
|||
|
|
try:
|
|||
|
|
fix_response, _ = llm.llm_call([{"role": "user", "content": fix_prompt}], self.model)
|
|||
|
|
fix_codes = llm.extract_code(fix_response, "verilog")
|
|||
|
|
if fix_codes:
|
|||
|
|
fixed_code = fix_codes[0]
|
|||
|
|
ls.save_code(fix_prompt, os.path.join(iter_dir, "compile_fix_prompt.txt"))
|
|||
|
|
ls.save_code(fix_response, os.path.join(iter_dir, "compile_fix_response.txt"))
|
|||
|
|
ls.save_code(fixed_code, os.path.join(iter_dir, "generated_scenario_compile_fix.v"))
|
|||
|
|
enhanced_tb = injector.inject(fixed_code, iter_idx=i)
|
|||
|
|
ls.save_code(enhanced_tb, os.path.join(iter_dir, "driver.v"))
|
|||
|
|
|
|||
|
|
# 再次尝试编译
|
|||
|
|
success, new_score, new_annotated_path = verilator_run_coverage(iter_dir, "DUT.v", "driver.v")
|
|||
|
|
if success:
|
|||
|
|
logger.info(f"[CGA-{i}] Compilation fixed! Score: {new_score:.2f}%")
|
|||
|
|
new_task_code = fixed_code
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.warning(f"[CGA-{i}] Compile fix attempt failed: {e}")
|
|||
|
|
self._compile_retry_count[i] += 1
|
|||
|
|
|
|||
|
|
coverage_snapshot = self._extract_coverage_snapshot(new_annotated_path) if success else {
|
|||
|
|
"covered_lines": set(),
|
|||
|
|
"covered_functions": set(),
|
|||
|
|
"coverable_lines": set(),
|
|||
|
|
}
|
|||
|
|
current_covered_lines = set(coverage_snapshot["covered_lines"])
|
|||
|
|
current_covered_functions = set(coverage_snapshot["covered_functions"])
|
|||
|
|
newly_covered_lines = current_covered_lines - self.best_covered_lines
|
|||
|
|
newly_covered_functions = current_covered_functions - self.best_covered_functions
|
|||
|
|
|
|||
|
|
# === [新增] 记录生成结果到能量分配器 ===
|
|||
|
|
coverage_delta = new_score - self.best_score if success else 0.0
|
|||
|
|
current_target_name = None
|
|||
|
|
target_hit = False
|
|||
|
|
if self.energy_allocator and self.energy_allocator.current_target:
|
|||
|
|
current_target_name = self.energy_allocator.current_target.function_point
|
|||
|
|
if current_covered_functions:
|
|||
|
|
target_hit = current_target_name in current_covered_functions
|
|||
|
|
else:
|
|||
|
|
target_hit = success and new_score > self.best_score
|
|||
|
|
generation_success = success and target_hit
|
|||
|
|
|
|||
|
|
if self.energy_allocator:
|
|||
|
|
self.energy_allocator.record_generation(
|
|||
|
|
success=generation_success,
|
|||
|
|
coverage_delta=coverage_delta,
|
|||
|
|
energy_cost=1.0
|
|||
|
|
)
|
|||
|
|
extra_completed_functions = set(newly_covered_functions)
|
|||
|
|
if generation_success and current_target_name:
|
|||
|
|
extra_completed_functions.discard(current_target_name)
|
|||
|
|
if extra_completed_functions:
|
|||
|
|
self.energy_allocator.mark_targets_completed(sorted(extra_completed_functions))
|
|||
|
|
# =========================================
|
|||
|
|
|
|||
|
|
# === [新增] 记录测试用例到多样性历史 ===
|
|||
|
|
if self.diversity_injector:
|
|||
|
|
# 提取已知信号
|
|||
|
|
known_signals = []
|
|||
|
|
if self.semantic_result:
|
|||
|
|
known_signals = [p.get('name', '') for p in self.semantic_result.get('ports', [])]
|
|||
|
|
|
|||
|
|
self.diversity_injector.record_test(
|
|||
|
|
code=new_task_code,
|
|||
|
|
target_function=self.energy_allocator.current_target.function_point if self.energy_allocator and self.energy_allocator.current_target else "",
|
|||
|
|
coverage_score=new_score,
|
|||
|
|
success=generation_success,
|
|||
|
|
iteration=i,
|
|||
|
|
known_signals=known_signals
|
|||
|
|
)
|
|||
|
|
# =======================================
|
|||
|
|
|
|||
|
|
# === [新增] Layer 3: 质量评估 ===
|
|||
|
|
if self.quality_evaluator:
|
|||
|
|
# 评估测试用例质量
|
|||
|
|
eval_result = self.quality_evaluator.evaluate_test_case(
|
|||
|
|
code=new_task_code,
|
|||
|
|
covered_lines=newly_covered_lines,
|
|||
|
|
covered_functions=sorted(newly_covered_functions),
|
|||
|
|
test_id=f"iter_{i}",
|
|||
|
|
iteration=i
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# 记录多样性得分
|
|||
|
|
diversity_score = eval_result.get('diversity', {}).get('overall_score', 0)
|
|||
|
|
logger.info(f" Quality Evaluation: diversity={diversity_score:.2f}")
|
|||
|
|
|
|||
|
|
# 检查是否应该接受该测试用例
|
|||
|
|
should_accept, reason = self.quality_evaluator.should_accept(eval_result)
|
|||
|
|
if not should_accept:
|
|||
|
|
logger.warning(f" Quality check failed: {reason}")
|
|||
|
|
# =====================================
|
|||
|
|
|
|||
|
|
if success and new_score > self.best_score:
|
|||
|
|
improvement = new_score - self.best_score
|
|||
|
|
logger.success(f"Coverage Improved! +{improvement:.2f}% ({self.best_score:.2f}% -> {new_score:.2f}%)")
|
|||
|
|
self.best_score = new_score
|
|||
|
|
self.best_tb = enhanced_tb
|
|||
|
|
last_annotated_file = new_annotated_path
|
|||
|
|
self.best_covered_lines = current_covered_lines
|
|||
|
|
self.best_covered_functions = current_covered_functions
|
|||
|
|
elif success and new_score == self.best_score:
|
|||
|
|
logger.info(f"Coverage unchanged. Keeping previous.")
|
|||
|
|
else:
|
|||
|
|
logger.warning(f"Regression or Failure. Discarding changes.")
|
|||
|
|
|
|||
|
|
if self.best_score >= self.target_coverage:
|
|||
|
|
logger.success("Target coverage reached!")
|
|||
|
|
break
|
|||
|
|
|
|||
|
|
logger.info(f"CGA Finished. Final Coverage: {self.best_score:.2f}%")
|
|||
|
|
|
|||
|
|
# === [新增] 生成能量分配报告 ===
|
|||
|
|
if self.energy_allocator:
|
|||
|
|
energy_report = self.energy_allocator.generate_report()
|
|||
|
|
ls.save_code(energy_report, os.path.join(work_dir, "energy_report.txt"))
|
|||
|
|
logger.info(f"Energy report saved to {work_dir}/energy_report.txt")
|
|||
|
|
# =================================
|
|||
|
|
|
|||
|
|
# === [新增] 生成多样性报告并保存历史 ===
|
|||
|
|
if self.diversity_injector:
|
|||
|
|
diversity_report = self.diversity_injector.generate_diversity_report()
|
|||
|
|
ls.save_code(diversity_report, os.path.join(work_dir, "diversity_report.txt"))
|
|||
|
|
logger.info(f"Diversity report saved to {work_dir}/diversity_report.txt")
|
|||
|
|
|
|||
|
|
# 保存测试历史
|
|||
|
|
self.diversity_injector.history.save()
|
|||
|
|
# ======================================
|
|||
|
|
|
|||
|
|
# === [新增] Layer 3: 生成质量评估报告 ===
|
|||
|
|
if self.quality_evaluator:
|
|||
|
|
quality_report = self.quality_evaluator.generate_report()
|
|||
|
|
ls.save_code(quality_report, os.path.join(work_dir, "quality_evaluation_report.txt"))
|
|||
|
|
logger.info(f"Quality evaluation report saved to {work_dir}/quality_evaluation_report.txt")
|
|||
|
|
|
|||
|
|
# 输出语义覆盖率摘要
|
|||
|
|
coverage_result = self.quality_evaluator.semantic_coverage.calculate_coverage()
|
|||
|
|
logger.info(f"Semantic Coverage: {coverage_result.semantic_coverage:.2%}")
|
|||
|
|
# ===========================================
|
|||
|
|
|
|||
|
|
# [修改] 返回元组 (代码, 分数)
|
|||
|
|
return self.best_tb, self.best_score
|