move baseline into class, support for pattern list import, fault list export, avoid resolving circuits twice.
This commit is contained in:
1 parent
5ba1f11d1d
commit
9a41edda4c
7 files changed
+149
-78
No files matched your search
@@ -0,0 +1,47 @@
|
||||
import time
|
||||
from collections.abc import Collection
|
||||
|
||||
import numpy as np
|
||||
|
||||
from kyupy import log, batchrange
|
||||
from kyupy.circuit import Circuit
|
||||
from kyupy.logic_sim import LogicSim2V
|
||||
|
||||
class SAFSimSimple:
|
||||
|
||||
def __init__(self, circuit_resolved: Circuit, batch_size: int):
|
||||
self.sim = LogicSim2V(circuit_resolved, sims=batch_size)
|
||||
self.sim_time = 0
|
||||
pass
|
||||
|
||||
def classify_faults(self, faults: Collection[int], patterns: np.ndarray):
|
||||
|
||||
golden = np.zeros_like(patterns)
|
||||
self.sim.simulate(patterns, golden)
|
||||
log.info(f'golden sim finished.')
|
||||
|
||||
syndrome = np.zeros_like(patterns)
|
||||
|
||||
fclass_NO = set()
|
||||
fclass_DS = set()
|
||||
|
||||
start_time = time.perf_counter()
|
||||
|
||||
with log.progress() as p:
|
||||
for fidx, fault in enumerate(faults):
|
||||
fault_site = fault//2
|
||||
fault_polarity = fault&1
|
||||
p.update((fidx+1) / len(faults), f'DS:{len(fclass_DS)} NO:{len(fclass_NO)}')
|
||||
for bo, bs in batchrange(patterns.shape[1], self.sim.sims):
|
||||
self.sim.s_assign[:, :bs] = patterns[:, bo:bo+bs]
|
||||
self.sim.s_to_c()
|
||||
self.sim.c_prop(fault_line=fault_site, fault_model=fault_polarity)
|
||||
self.sim.c_to_s()
|
||||
syndrome[:, bo:bo+bs] = self.sim.s_result[:,:bs]
|
||||
if np.allclose(golden, syndrome):
|
||||
fclass_NO.add(fault)
|
||||
else:
|
||||
fclass_DS.add(fault)
|
||||
|
||||
self.sim_time += time.perf_counter() - start_time
|
||||
return {'DS': fclass_DS, 'NO': fclass_NO}
|
||||
+22
-21
@@ -90,7 +90,7 @@ class FaultSet:
|
||||
Stuck-at faults are collapsed using basic controlling-value theory of simple gates.
|
||||
"""
|
||||
|
||||
def __init__(self, circuit: Circuit, tlib: TechLib):
|
||||
def __init__(self, circuit: Circuit, tlib: TechLib, circuit_resolved: Circuit):
|
||||
lr = LineRoles(circuit, tlib)
|
||||
|
||||
self.fault_sites = {l.index for l in circuit.lines
|
||||
@@ -117,22 +117,22 @@ class FaultSet:
|
||||
representative f always contains at least f (:math:`f \in` saf_equiv_classes[f]).
|
||||
"""
|
||||
|
||||
def collect_equivalent_faults(circuit: Circuit, fault: int) -> set:
|
||||
def collect_equivalent_faults(circuit_resolved: Circuit, fault: int) -> set:
|
||||
site = fault//2
|
||||
polarity = fault&1
|
||||
driver = circuit.lines[site].driver
|
||||
driver = circuit_resolved.lines[site].driver
|
||||
# Stop collapsing at fanout stems, primary inputs, and cells whose
|
||||
# output faults have no equivalent single-input fault (XOR/XNOR, MUX, the two-term
|
||||
# AND-OR/OR-AND cells, and sequential elements).
|
||||
if len(driver.outs) > 1 or len(driver.ins) == 0 or driver.kind in ('XOR2', 'XOR3', 'XOR4', 'XNOR2', 'XNOR3', 'XNOR4', 'MUX21', 'AO22', 'AOI22', 'OA22', 'OAI22', 'DFF', 'LATCH'): return {fault}
|
||||
if driver.kind == 'INV1': return {fault} | collect_equivalent_faults(circuit, driver.ins[0].index*2 + (1-polarity))
|
||||
if driver.kind in ('BUF1', '__fork__'): return {fault} | collect_equivalent_faults(circuit, driver.ins[0].index*2 + (polarity))
|
||||
if driver.kind == 'INV1': return {fault} | collect_equivalent_faults(circuit_resolved, driver.ins[0].index*2 + (1-polarity))
|
||||
if driver.kind in ('BUF1', '__fork__'): return {fault} | collect_equivalent_faults(circuit_resolved, driver.ins[0].index*2 + (polarity))
|
||||
|
||||
def collapse(*faults: tuple) -> set:
|
||||
"""Collapse the output fault onto the given (input_line, stuck_value) pairs."""
|
||||
equiv = {fault}
|
||||
for site, value in faults:
|
||||
equiv |= collect_equivalent_faults(circuit, site.index*2 + value)
|
||||
equiv |= collect_equivalent_faults(circuit_resolved, site.index*2 + value)
|
||||
return equiv
|
||||
|
||||
# Simple gates: driving any input to its controlling value forces the output.
|
||||
@@ -151,13 +151,13 @@ class FaultSet:
|
||||
|
||||
# AND-OR / OR-AND complex gates: only the "single" (un-paired) terms force the output.
|
||||
# AO21 = (i0&i1) | i2 : out s-a-1 == i2 s-a-1
|
||||
# AOI21 = ~AO21 : out s-a-0 == i2 s-a-1
|
||||
# AOI21 = ~AO21 : out s-a-0 == i2 s-a-1
|
||||
# OA21 = (i0|i1) & i2 : out s-a-0 == i2 s-a-0
|
||||
# OAI21 = ~OA21 : out s-a-1 == i2 s-a-0
|
||||
# OAI21 = ~OA21 : out s-a-1 == i2 s-a-0
|
||||
# AO211 = (i0&i1) | i2 | i3 : out s-a-1 == i2 s-a-1, i3 s-a-1
|
||||
# AOI211= ~AO211 : out s-a-0 == i2 s-a-1, i3 s-a-1
|
||||
# AOI211= ~AO211 : out s-a-0 == i2 s-a-1, i3 s-a-1
|
||||
# OA211 = (i0|i1) & i2 & i3 : out s-a-0 == i2 s-a-0, i3 s-a-0
|
||||
# OAI211= ~OA211 : out s-a-1 == i2 s-a-0, i3 s-a-0
|
||||
# OAI211= ~OA211 : out s-a-1 == i2 s-a-0, i3 s-a-0
|
||||
if driver.kind == 'AO21': return collapse((driver.ins[2], 1)) if polarity == 1 else {fault}
|
||||
if driver.kind == 'AOI21': return collapse((driver.ins[2], 1)) if polarity == 0 else {fault}
|
||||
if driver.kind == 'OA21': return collapse((driver.ins[2], 0)) if polarity == 0 else {fault}
|
||||
@@ -167,11 +167,9 @@ class FaultSet:
|
||||
if driver.kind == 'OA211': return collapse((driver.ins[2], 0), (driver.ins[3], 0)) if polarity == 0 else {fault}
|
||||
if driver.kind == 'OAI211': return collapse((driver.ins[2], 0), (driver.ins[3], 0)) if polarity == 1 else {fault}
|
||||
|
||||
raise ValueError(f'Unknown node kind {driver.kind}. Circuit should have been resolved to techlib.KYUPY?')
|
||||
raise ValueError(f'Unknown node kind {driver.kind}. circuit_resolved must be resolved to techlib.KYUPY with resolve_tlib_cells().')
|
||||
|
||||
remaining = self.saf_set.copy()
|
||||
circuit_resolved = circuit.copy()
|
||||
circuit_resolved.resolve_tlib_cells(tlib)
|
||||
for n in circuit.reversed_topological_order(tlib):
|
||||
for il in n.ins.without_nones():
|
||||
for polarity in (0, 1):
|
||||
@@ -181,13 +179,16 @@ class FaultSet:
|
||||
remaining.difference_update(equiv)
|
||||
self.saf_equiv_classes[repr] = equiv
|
||||
|
||||
self.circuit = circuit
|
||||
self.tlib = tlib
|
||||
|
||||
def fault_str(self, fault: int) -> str:
|
||||
site = self.circuit.lines[fault//2]
|
||||
polarity = '@1' if fault&1 else '@0'
|
||||
@staticmethod
|
||||
def fault_site_str(circuit: Circuit, tlib: TechLib, fault: int) -> str:
|
||||
site = circuit.lines[fault//2]
|
||||
if site.driver.kind == '__fork__':
|
||||
return f'{site.reader.name}/{self.tlib.pin_name(site.reader.kind, site.reader_pin)}{polarity}'
|
||||
return f'{site.reader.name}/{tlib.pin_name(site.reader.kind, site.reader_pin)}'
|
||||
elif site.driver.kind == 'input':
|
||||
return f'{site.driver.name}'
|
||||
else:
|
||||
return f'{site.driver.name}/{self.tlib.pin_name(site.driver.kind, site.driver_pin, output=True)}{polarity}'
|
||||
return f'{site.driver.name}/{tlib.pin_name(site.driver.kind, site.driver_pin, output=True)}'
|
||||
|
||||
@staticmethod
|
||||
def fault_type_str(fault: int) -> str:
|
||||
return 'sa1' if fault&1 else 'sa0'
|
||||
Reference in new issue
Block a user