☰
DRAM故障建模与March算法:从故障注入到测试选型
2026/10/28 15:17:07 网站建设 项目流程

简介:这份PPT资源聚焦内存的故障模型与测试算法设计,面向从事存储器测试、BIST设计与嵌入式内存验证的工程人员及高年级学习者,帮助其建立从功能模型到故障类型再到测试算法的完整认知框架。内容围绕内存二维位阵列的读写工作模式展开,系统梳理单单元静态故障(粘滞、过渡、开路、数据保持)、耦合与桥接故障、邻域模式敏感故障以及动态故障等模型,并讲解March系列经典测试算法的序列构造与时间复杂度,同时介绍RAMFLT工具在故障覆盖率分析与算法排序中的用法,覆盖确定性测试与伪随机测试的对比思路。资源包共1个PPT文件,约238KB,以图形式提纲串联动机、故障模型、方法与复杂度、仿真结果与结论各部分,便于课堂讲授或自学时按页索引关键知识点。目前已有336人学习,适合用作内存测试方向的入门梳理与算法设计参考资料。

1. 内存故障模型与算法设计:为什么先画故障地图再写测试代码

内存的故障模型和算法设计,说到底是在回答两个问题:单元会怎么坏,读写序列怎么把它逼出来。很多人一上来就抄 March 序列,结果测试跑了很久,地址译码故障和保持时间故障还是漏;也有人把仿真覆盖率做到很高,上板后却被刷新开销和温度窗口打回原形。故障模型是地图,算法是路线,地图画错,路线越努力越偏。这里面向做芯片验证、板级诊断、BIST 配置和可靠性评估的读者,把 DRAM 常见故障拆成可编码的模型,再用 Python 写一个最小 March 执行器,落到算法选型、参数设置和排查习惯。你不需要先有完整测试机台,有一块能跑仿真的环境就能跟着做。

2. 把 DRAM 故障拆成可编码的六类模型:从固定故障到保持时间

内存测试的第一步不是选算法,而是把故障写成数据结构。物理层的失效表现为电荷泄漏、写驱动不足、邻接干扰、译码错位,到了测试层必须变成地址、数据、操作顺序和观测点。没有这张 fault 表,March 序列就只是字符串,覆盖率也没法解释。我一般先把故障分成六类:固定故障、跳变故障、耦合故障、邻域模式敏感故障、地址译码故障、干扰与保持时间故障。前四类适合用 March 和棋盘类算法覆盖,后两类需要单独的触发窗口和参数扫描。下面按“能不能用地址和数据表达”这条线拆开。

2.1 固定故障、跳变故障与耦合故障怎么写进一张 fault 表

固定故障(SAF)是单元恒为 0 或恒为 1,读回值不随写入变化。跳变故障(TF)是单元能保持某个值,但 0→1 或 1→0 的写操作失败,常见原因是写驱动弱或电荷注入不足。耦合故障(CF)是一个单元的状态或操作影响另一个单元,攻击单元叫 aggressor,受害单元叫 victim。三者都能用一条记录表达:类型、受害地址、攻击地址、触发状态、触发操作、观测值。字段不一定要多,但必须能回放,否则排查时只能靠猜。

故障类型抽象表达触发条件观测点
SAFcell[a] 恒为 v任意访问读回值不等于期望
TF写 v 后仍保持 ~v0→1 或 1→0 写写后立即读
CFvictim 受 aggressor 状态影响aggressor 满足指定状态victim 读或写之后
NPSFvictim 受邻域 pattern 影响邻居组合满足模式邻域填充后读 victim
AF地址 a 实际落到 b地址线或译码器异常地址签名读回错位
Retention写后一段时间数据丢失等待超过保持时间等待窗口后读

地址空间表达是第二层。物理地址通常拆成 bank、row、col,测试算法看到的是一维线性地址。我一般写一个映射函数linear = bank * rows * cols + row * cols + col,再用它把物理邻接关系转成算法能遍历的地址邻接。参数上先确认地址位宽、行列数、数据位宽,再决定 fault 表里存线性地址还是三维坐标。存三维坐标更利于分析邻域故障,存线性地址更利于 March 执行器直接遍历。我的做法是 fault 表存三维坐标,执行前统一转线性地址,避免两套索引打架。

可执行步骤不复杂:先把失效样本归类到六类模型,给每条故障填 victim、aggressor、触发状态和观测操作;再写地址映射函数,把 row/col 邻接转成线性地址差;最后用一个最小执行器注入故障,确认读回值和预期不一致。这里最常见的错误是把 SAF 注入在写操作之后,结果写操作把故障值覆盖掉,仿真看起来“通过”,实际是注入顺序错了。fault 表里要写明注入时机,读时注入还是写时注入,不能只写类型。

2.2 邻域模式敏感故障与地址译码故障的矩阵化参数

邻域模式敏感故障(NPSF)是 victim 单元受周围单元 pattern 影响,单看 victim 的读写序列测不出来。工程上不会枚举整个阵列的所有组合,而是取一个邻域模板,常见是 3×3 去掉中心,剩 8 个邻居,模式数 2^8=256;如果只取上下左右 4 个邻居,模式数降到 16,覆盖会弱一些但测试时间可控。参数 k 就是邻居数量,k 越大覆盖越强,测试时间按 2^k 增长。我一般先跑 k=4 的快速筛查,再对可疑地址跑 k=8 的定向复测,避免一上来就全阵列枚举。

邻域模板要落到矩阵。给每个 victim 地址维护一个邻居列表,写测试时先按 pattern 填邻居,再读 victim。棋盘子(checkerboard)适合测相邻单元同时翻转的耦合,走 1(walking 1)适合测单个单元对邻居的影响,GALPAT 适合测更复杂的模式序列。参数上要记录背景数据、邻居填充顺序、victim 读之前是否插入延迟。NPSF 的触发往往依赖写顺序,先写 victim 再写邻居和先写邻居再写 victim,结果可能不一样,fault 表里要带trigger_order字段。

地址译码故障(AF)是另一条线。它表现为访问地址 a 实际落到地址 b,或者地址线 stuck 导致一段地址重复映射。March 序列能覆盖一部分 AF,但前提是数据背景能让错位暴露出来。如果所有地址都写 0,地址错位读回还是 0,测试就漏了。常见做法是给每个地址写地址签名,比如data = addr ^ mask,读回再比较;再加一轮反码~addr,覆盖地址线 stuck-at-1 和 stuck-at-0。参数是签名位宽和掩码,签名位宽要小于等于数据位宽,掩码用来避免全 0 和全 1 的退化模式。

AF 子类表现检测手段关键参数
地址线 stuck一段地址重复映射地址签名、反码签名签名位宽、掩码
译码器错位访问 a 落到 b走 1 地址、移动反转地址步长、遍历顺序
地址别名两个地址读到同一单元唯一数据背景数据位宽、背景种子

这两类故障放在一起讲,是因为它们都要求算法“看见地址”,而不是只看见数据。March 的元素序列如果只写固定 0/1,对 AF 的覆盖有限;加一段地址签名测试,往往比把 March 序列加长更有效。我一般把地址签名放在 March 之前做预筛,签名失败再进详细诊断,这样量产测试时间不会失控。

2.3 行锤、读干扰与保持时间故障的触发窗口

干扰类故障和保持时间故障不属于传统的逻辑故障模型,它们依赖时间、温度和访问历史。行锤干扰是频繁激活某一行,导致相邻行的电荷状态受影响,触发条件是激活次数超过阈值;读干扰是频繁读同一行,影响邻近行的读余量;保持时间故障是写后等待一段时间,数据自然丢失,弱单元和可变保持时间单元会让这个窗口忽长忽短。测试这类故障,算法设计重点不是读写序列多复杂,而是触发窗口怎么设。

行锤测试一般用激活循环:对目标行连续激活,中间插入刷新或空操作,再读相邻行校验。参数包括激活次数阈值、每次激活之间的间隔、刷新插入频率、温度点。激活次数阈值不能拍脑袋,要从工艺数据和失效样本里反推;间隔太短会把时序冲突当成干扰故障,间隔太长又测不出真实阈值。我一般先做阈值扫描,从低次数往高次数跑,记录首次失效点,再把阈值下移到安全边界做批量筛查。

保持时间测试的流程更简单:写背景数据,等待窗口 t,读回校验。难点在 t 怎么设。t 要覆盖最坏刷新间隙,通常按刷新间隔的倍数取多个点,而不是只取一个固定值。温度要分组,高温会加速电荷泄漏,常温通过不代表高温通过。弱单元的可变保持时间会让同一个地址在不同轮次表现不同,所以要保持时间测试重复多轮,记录失败率而不是单次通过与否。参数表里至少要有等待窗口、温度点、重复轮次、刷新间隔,缺一个都很难复现。

参数作用设置思路
激活次数阈值行锤触发强度从失效样本反推,再做安全下移
等待窗口保持时间测试长度取刷新间隔的多个倍数,覆盖最坏间隙
温度点加速电荷泄漏常温、高温分组,分别统计失败率
重复轮次捕获可变保持时间同一地址多轮测试,记录失败次数
刷新插入频率避免正常刷新掩盖干扰测试模式与正常刷新模式分开跑

注意:干扰类和保持时间类故障的测试窗口一旦和正常刷新、后台巡检混在一起,失败原因就很难归因。测试模式要能关掉或固定后台刷新,否则你看到的是“系统行为”,不是“单元行为”。

3. March 算法族的最小实现:用 Python 跑通 March C- 与参数化序列

选 March 算法不是因为它名字多,而是因为它把地址遍历、数据背景和操作顺序压成了一个可执行序列。March 元素由若干个操作组成,每个操作是读或写,读要带期望值,写要带数据值;元素还带地址顺序,升序、降序或任意。一个 March 算法就是元素列表,执行器按顺序遍历地址。这个结构足够简单,能在仿真里快速换序列,也足够表达大多数固定故障、跳变故障和耦合故障的检测条件。下面用 Python 写一个一维阵列和故障注入器,先把 March C- 跑通,再参数化一个 SR 风格序列做时间估算。

3.1 内存阵列与故障注入器的数据结构

先定义Fault和Memory。Fault存类型、地址、值、攻击地址、触发操作;Memory存单元数组、故障列表和错误列表。读写时调用_apply_faults注入故障。这里刻意做得简化:位宽默认 1 bit,地址一维,故障按列表顺序生效。真实项目里要加 bank/row/col 映射和时序模型,但最小闭环先保证能复现漏检。

from dataclasses import dataclass from typing import Optional, List, Tuple @dataclass class Fault: kind: str # SAF / TF / CF addr: int # 受害地址 value: Optional[int] = None aggressor: Optional[int] = None trigger: Optional[str] = None # 触发操作,如 "r" 或 "w" class Memory: def __init__(self, n: int, bits: int = 1): self.n = n self.mask = (1 << bits) - 1 self.cells = [0] * n self.faults: List[Fault] = [] self.errors: List[Tuple[int, str, int, int]] = [] def add_fault(self, f: Fault): self.faults.append(f) def _apply_faults(self, addr: int, op: str): for f in self.faults: if f.kind == "SAF" and f.addr == addr: # 固定故障:读或写都强制成故障值 self.cells[addr] = f.value & self.mask elif f.kind == "TF" and f.addr == addr and op == "w": # 跳变故障:写操作失败,保持旧值 # 这里用 trigger 区分 0->1 或 1->0,实际项目要记录旧值 if f.trigger == "write": pass elif f.kind == "CF" and f.addr == addr and op == "r": # 耦合故障:攻击单元满足状态时,受害单元翻转 if f.aggressor is not None and self.cells[f.aggressor] == f.value: self.cells[addr] ^= 1 def write(self, addr: int, data: int): self.cells[addr] = data & self.mask self._apply_faults(addr, "w") def read(self, addr: int) -> int: self._apply_faults(addr, "r") return self.cells[addr]

逻辑说明:write先写正常值,再应用故障;read先应用读触发故障,再返回值。参数n是地址数量,bits是数据位宽,mask用来截断数据。SAF 在读写时都会覆盖,所以注入顺序会影响结果;TF 这里只留了触发标记,真实实现要比较写入前后的值;CF 在读取受害地址时检查攻击地址状态,状态满足就翻转。这个注入器不追求器件级精确,但能用来验证算法是否覆盖某类故障,尤其是看漏检发生在哪个地址、哪个操作。

3.2 March C- 的元素序列与地址顺序怎么执行

March C- 常见的元素序列是:任意顺序写 0,升序读 0 写 1,升序读 1 写 0,降序读 0 写 1,降序读 1 写 0,任意顺序读 0。总共 10N 次操作:第一段和最后一段各 N 次,中间四段各 2N 次。执行器要支持asc、desc、any三种地址顺序,any在实现里按升序处理,真实项目里可以随机化以覆盖地址顺序相关故障。

def march_run(mem: Memory, elements: list): for elem in elements: order = elem["order"] ops = elem["ops"] if order == "asc" or order == "any": addrs = range(mem.n) elif order == "desc": addrs = range(mem.n - 1, -1, -1) else: raise ValueError(order) for a in addrs: for op, val in ops: if op == "w": mem.write(a, val) elif op == "r": got = mem.read(a) if got != val: mem.errors.append((a, "read", val, got)) MARCH_C_MINUS = [ {"order": "any", "ops": [("w", 0)]}, {"order": "asc", "ops": [("r", 0), ("w", 1)]}, {"order": "asc", "ops": [("r", 1), ("w", 0)]}, {"order": "desc", "ops": [("r", 0), ("w", 1)]}, {"order": "desc", "ops": [("r", 1), ("w", 0)]}, {"order": "any", "ops": [("r", 0)]}, ]

逻辑说明:每个元素先决定地址顺序,再按地址遍历,每个地址内按ops顺序执行读写。读操作比较期望值,失败就记录(地址, 操作, 期望, 实际)。参数上,order影响耦合故障的触发方向,ops里的期望值是数据背景,不能随便改。March C- 对 SAF 和多数 TF 覆盖较好,但对地址译码故障和部分 NPSF 覆盖不足,所以它常作为基线,不是万能算法。跑完以后看mem.errors,如果为空不代表没有故障,可能是故障模型没注入进去,或者注入顺序被后续写操作覆盖了。

3.3 参数化 March 序列与测试时间估算公式

有了执行器,就可以把序列参数化。比如在 March C- 基础上加一段延迟读,或者把某个元素拆成两次读,用来覆盖保持时间相关的弱单元。下面这个MARCH_SR_LIKE不是某个标准的逐字复刻,而是一个可配置的 SR 风格序列:在升序和降序段之间插入等待,读操作重复两次,方便观察读干扰和保持时间。真实项目里按故障模型增删元素,不要为了名字好听硬凑序列。

MARCH_SR_LIKE = [ {"order": "asc", "ops": [("w", 0)]}, {"order": "asc", "ops": [("r", 0), ("w", 1)]}, {"order": "asc", "ops": [("r", 1), ("r", 1)]}, {"order": "desc", "ops": [("r", 1), ("w", 0)]}, {"order": "desc", "ops": [("r", 0), ("r", 0)]}, {"order": "asc", "ops": [("r", 0)]}, ] def estimate_time(n: int, elements: list, t_cmd_ns: float, refresh_overhead: float = 0.05, wait_ns: float = 0.0) -> float: ops = sum(len(e["ops"]) for e in elements) base = n * ops * t_cmd_ns total = base * (1.0 + refresh_overhead) + wait_ns return total # 示例:1024 个地址,命令间隔按 10 ns 估算,刷新开销 5% t = estimate_time(1024, MARCH_C_MINUS, 10.0, 0.05) print(t) # 单位 ns

逻辑说明:ops统计每个地址平均执行多少次读写,base是纯命令时间,refresh_overhead把刷新和命令开销折算进去,wait_ns是保持时间等待。参数t_cmd_ns不是单个读写的理论时间,而是命令级间隔,包含行激活、列访问和总线传输的等效开销;refresh_overhead不要设成 0,否则估算会偏乐观。这个公式适合做算法之间的相对比较,不适合替代机台实测。血泪经验是:仿真里 10N 很快,上板后刷新、时序和后台任务一叠,测试时间经常翻倍,估算时宁可留余量。

4. 测试算法选型:覆盖率、复杂度与在线检测的三角平衡

算法选型不是“哪个名字高级用哪个”,而是看故障模型、测试预算和诊断分辨率。量产筛选要的是在固定时间内覆盖大概率故障,诊断复测要的是定位到具体地址和故障类型,在线检测要的是低开销、可切片、不干扰正常访问。三者目标不同,算法就不能一套通吃。我一般先列故障模型,再给每个模型配一个主算法和一个补充算法,最后用测试时间公式卡预算。下面这张映射表不是标准答案,但能帮你把选型从感觉变成可讨论的参数。

4.1 故障模型到测试算法的映射表

故障模型主算法补充算法复杂度备注
SAFMarch C-棋盘O(n)固定 0/1 背景即可覆盖多数
TFMarch C-March SR 风格O(n)关注写后读的期望值
CFMarch C-走 1、GALPATO(n) 到 O(n²)地址顺序影响触发方向
NPSF棋盘、走 1邻域模板枚举O(n·2^k)k 是邻居数,按预算取 4 或 8
AF地址签名移动反转、走 1 地址O(n) 到 O(n²)数据背景要唯一化
Retention写后等待读多窗口扫描O(n·窗口数)温度分组,重复多轮
Disturb激活循环读干扰循环O(n·次数)与刷新策略联动

这张表里,March C- 是基线,不是全部。SAF 和 TF 用 March C- 性价比高;CF 要看耦合方向,升序和降序都要有;NPSF 和 AF 需要额外序列,不能指望 March C- 全包。参数上,复杂度 O(n) 表示操作数随地址数线性增长,O(n²) 表示每个地址都要对其他地址做操作,测试时间会快速膨胀。选型时先问测试预算允不允许 O(n²),再问诊断需不需要定位到具体邻居。

4.2 O(n) 与 O(n²) 的取舍:棋盘、走 1 和 March 各管什么

棋盘算法的做法是相邻单元写相反数据,然后读回校验,复杂度 O(n),对相邻单元短路、耦合和部分 NPSF 有效。它的弱点是模式单一,遇到依赖特定邻居组合的故障会漏。走 1 是让一个单元为 1、其余为 0,然后逐个移动 1 的位置,复杂度 O(n²),对单单元干扰和地址译码故障诊断更细,但量产全跑不现实。GALPAT 更偏向模式序列覆盖,时间也高。我的取舍是:量产用 March C- 加棋盘做 O(n) 筛查,诊断阶段对可疑 bank 或 row 用走 1 做 O(n²) 复测,在线检测用切片 March,不碰走 1。

参数上,棋盘要定背景数据、行列方向和是否翻转;走 1 要定步长、起始地址和是否同时走走 0;March 要定元素序列、地址顺序和数据背景。测试时间预算紧的时候,先砍 O(n²) 的全阵列遍历,保留 O(n) 的主序列,再把走 1 限制在失败地址附近的小窗口。这样既能保住覆盖率,又不会让测试时间失控。不要为了追求“全覆盖”把走 1 塞进每次上电自检,用户体验和功耗都会翻车。

4.3 在线检测与 BIST 的四个调度参数

在线检测和量产测试不是一回事。在线检测要插在正常访问的空隙里,不能长时间占用内存,所以 March 序列要切片,地址游标要能断点续跑。BIST 调度一般看四个参数:切片大小、巡检间隔、错误阈值、重试次数。切片大小决定单次占用多少地址,巡检间隔决定多久轮一遍,错误阈值决定什么时候上报,重试次数决定瞬态错误要不要计数。参数设得太激进,误报多;设得太保守,故障发现晚。

参数作用调整方向
slice_size单次巡检地址数访问密集时调小,空闲时调大
scrub_interval全阵列轮询间隔可靠性要求高时缩短,功耗敏感时放长
error_threshold上报阈值先按可纠正错误计数,再看是否升级
retry_count瞬态错误重试至少 1 次,避免单次读干扰误判

可执行步骤:先把长 March 序列拆成固定地址窗口的切片,维护一个游标;每次空闲周期执行一个切片,记录错误地址和操作;同一地址连续多轮失败才升级为硬故障,单次失败进重试队列;最后把切片参数和错误统计写进日志,方便回放。注意在线检测会与正常访问争用带宽,切片太大反而影响业务,参数要按实际负载调,不要照搬量产测试的序列长度。

5. 避坑与排查:内存故障建模和算法设计里最容易翻车的五件事

排查顺序我一般按“模型对不对、算法覆没覆盖、时间模型准不准、在线误报是不是混进来”走。很多问题不是算法写错,而是故障模型和观测点对不上。下面五条是实际做下来最容易翻车的点,每条按现象、原因、解决写,能帮你少走一点弯路。

5.1 地址译码故障被 March 序列漏掉

现象是 March C- 全过,换到地址签名测试才暴露出某段地址重复映射,或者地址线交换后某些地址永远读不到。原因是 March 序列的数据背景太单一,全 0 或全 1 下地址错位读回值仍然相同,算法看不见错位。解决是加地址签名测试:给每个地址写addr ^ mask,读回比较;再加一轮反码,覆盖地址线 stuck-at-1 和 stuck-at-0。签名位宽不要超过数据位宽,掩码要避开全 0 和全 1 的退化模式。如果地址空间大,可以先按 bank 或 row 分组做签名,再对失败组做走 1 地址复测,不要一上来就全阵列走 1。

5.2 保持时间测试的等待窗口设错

现象是常温测试通过,高温批量出现保持时间失效;或者同一地址有时通过有时失败,复测抓不到。原因是等待窗口只取了一个固定值,没有覆盖最坏刷新间隙,也没有考虑温度对电荷泄漏的加速。解决是把等待窗口按刷新间隔的倍数取多个点,至少覆盖短、中、长三档;温度分组,常温和高分别统计;同一地址重复多轮,记录失败率而不是单次结果。弱单元的可变保持时间会让单次复测通过,所以判定要看多轮失败计数。测试时还要确认后台刷新有没有被关掉,否则正常刷新会把保持时间故障掩盖掉。

5.3 故障注入器把耦合故障方向写反

现象是注入 CF 后算法覆盖率异常高,或者换个地址顺序覆盖率突然掉下去。原因是 fault 表里 aggressor 和 victim 写反了,触发状态没满足,或者读操作和写操作的注入时机不对。解决是在 fault 表里显式记录aggressor、victim、trigger_op和trigger_order,回测时打印触发轨迹,确认攻击单元在受害单元读之前已经处于指定状态。March 的升序和降序会影响触发方向,注入器要按执行顺序更新状态,不能一次性把所有故障都应用完。调试时先跑单故障、单地址,确认注入生效,再跑全阵列。

5.4 测试时间估算忽略刷新与命令开销

现象是仿真里 10N 操作很快,上板实测慢一个数量级,测试窗口排不下。原因是时间估算只算了读写次数,没算行激活、预充电、刷新和总线传输,t_cmd_ns取成了理论最小间隔。解决是用命令级时间模型,把刷新开销折算成比例,保留等待窗口的独立项;先拿小阵列标定t_cmd_ns,再放大到全阵列。估算结果要留余量,尤其是带保持时间等待的序列,等待时间不能按 0 算。测试预算紧的时候,优先砍 O(n²) 序列,不要砍刷新开销,否则估算越算越乐观。

5.5 在线检测误报被当成硬故障

现象是在线 March 报错,离线复测通过;或者同一地址在业务高峰时报错,空闲时正常。原因是在线检测与正常访问冲突,读干扰、温度瞬态和 ECC 纠正后的残留状态被当成硬故障。解决是加错误计数和重试:单次失败先进重试队列,同一地址连续多轮失败才升级;区分可纠正错误和不可纠正错误,可纠正错误按阈值统计,不可纠正错误立即上报;巡检窗口避开业务高峰,或者把切片调小。在线检测的日志要带时间、温度、地址和操作类型,不然误报和真故障分不开,黑匣子也救不了。

6. 用故障注入回测算法:一套可复现的验证脚本与判定习惯

算法写完不算完,要用故障注入回测覆盖率。做法很直接:准备一组故障样本,每次只注入一个故障,跑目标算法,看错误列表是否命中;统计命中率,再按故障类型和地址分组看漏检。下面这个最小闭环够用,关键是每次新建内存实例,避免故障状态串味。

def backtest(mem_factory, faults, algorithm): hit = 0 miss = [] for f in faults: mem = mem_factory() mem.add_fault(f) mem.errors.clear() algorithm(mem) if mem.errors: hit += 1 else: miss.append(f) return hit / len(faults), miss # 用法:覆盖率, 漏检列表 = backtest(lambda: Memory(1024), faults, lambda m: march_run(m, MARCH_C_MINUS))

逻辑说明:mem_factory每次返回干净阵列,faults是故障样本列表,algorithm是执行函数。命中条件是错误列表非空,漏检就记录下来。参数上,故障样本要覆盖 SAF、TF、CF 和 AF,不能只注入 SAF 就宣称覆盖率高。覆盖率统计要分类型看,整体 90% 可能意味着 AF 全军覆没。漏检定位时把miss按地址和类型排序,先看是不是注入顺序被覆盖,再看算法序列有没有对应操作。

我现在的习惯是先把 fault 表写出来,再写算法;每次算法改动都跑一遍注入回测,不然很容易被高覆盖率骗过去。参数扫描也别一次只改一个值,地址顺序和数据背景要组合扫,漏检往往藏在组合里。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询