def Orr(in_, out): # INVAR: (in = 0) -> (out = 0) & (in != 0) -> (out = 1) vars_ = [in_, out] comment = "Orr (in, out) = (%s, %s)" % (tuple( [x.symbol_name() for x in vars_])) Logger.log(comment, 3) if (in_.symbol_type() == BOOL) and (out.symbol_type() == BOOL): invar = EqualsOrIff(in_, out) else: if out.symbol_type() == BOOL: out0 = Not(out) out1 = out else: out0 = EqualsOrIff(out, BV(0, 1)) out1 = EqualsOrIff(out, BV(1, 1)) true_res = Implies( EqualsOrIff(in_, BV(0, in_.symbol_type().width)), out0) false_res = Implies( Not(EqualsOrIff(in_, BV(0, in_.symbol_type().width))), out1) invar = And(true_res, false_res) ts = TS(comment) ts.vars, ts.invar = set(vars_), invar return ts
def Mux(in0, in1, sel, out): # if Modules.functional # INVAR: out' = Ite(sel = 0, in0, in1) # else # INVAR: ((sel = 0) -> (out = in0)) & ((sel = 1) -> (out = in1)) vars_ = [in0, in1, sel, out] comment = "Mux (in0, in1, sel, out) = (%s, %s, %s, %s)" % (tuple( [x.symbol_name() for x in vars_])) Logger.log(comment, 3) if sel.symbol_type() == BOOL: sel0 = Not(sel) sel1 = sel else: sel0 = EqualsOrIff(sel, BV(0, 1)) sel1 = EqualsOrIff(sel, BV(1, 1)) if Modules.functional: invar = And(EqualsOrIff(out, Ite(sel0, in0, in1))) else: invar = And(Implies(sel0, EqualsOrIff(in0, out)), Implies(sel1, EqualsOrIff(in1, out))) ts = TS(comment) ts.vars, ts.invar = set(vars_), invar return ts
def parse_string(self, contents:str)->HTS: ''' Parses a string representation of an initial state file ''' hts = HTS("INIT") ts = TS("TS INIT") init = [] for line in contents.split('\n'): line = line.strip() if not line: continue else: res = self.parse_line(line) if res is not None: init.append(res) Logger.msg("Initial state file set concrete values for {} state variables".format(len(init)), 1) ts.init = And(init) ts.invar = TRUE() ts.trans = TRUE() hts.add_ts(ts) return hts
def Neq(in0, in1, out): # INVAR: (((in0 != in1) -> (out = #b1)) & ((in0 == in1) -> (out = #b0))) vars_ = [in0, in1, out] comment = "Eq (in0, in1, out) = (%s, %s, %s)" % (tuple( [x.symbol_name() for x in vars_])) Logger.log(comment, 3) # TODO: Create functional encoding if Modules.functional: if out.symbol_type() == BOOL: invar = EqualsOrIff(out, Not(EqualsOrIff(in0, in1))) else: invar = EqualsOrIff(out, BVNot(BVComp(in0, in1))) else: eq = EqualsOrIff(in0, in1) if out.symbol_type() == BOOL: out0 = Not(out) out1 = out else: out0 = EqualsOrIff(out, BV(0, 1)) out1 = EqualsOrIff(out, BV(1, 1)) invar = And(Implies(Not(eq), out1), Implies(eq, out0)) ts = TS(comment) ts.vars, ts.invar = set(vars_), invar return ts
def Zext(in_, out): # INVAR: (<op> in) = out) vars_ = [in_, out] comment = ("ZExt (in, out) = (%s, %s)") % (tuple( [x.symbol_name() for x in vars_])) Logger.log(comment, 3) if (in_.symbol_type() == BOOL) and (out.symbol_type() == BOOL): invar = EqualsOrIff(in_, out) if (in_.symbol_type() != BOOL) and (out.symbol_type() == BOOL): invar = EqualsOrIff(BV2B(in_), out) if (in_.symbol_type() == BOOL) and (out.symbol_type() != BOOL): length = (out.symbol_type().width) - 1 if length == 0: invar = EqualsOrIff(in_, BV2B(out)) else: invar = EqualsOrIff(BVZExt(B2BV(in_), length), out) if (in_.symbol_type() != BOOL) and (out.symbol_type() != BOOL): length = (out.symbol_type().width) - (in_.symbol_type().width) if length == 0: invar = EqualsOrIff(in_, out) else: invar = EqualsOrIff(BVZExt(in_, length), out) ts = TS(comment) ts.vars, ts.invar = set(vars_), invar return ts
def Uop(bvop, bop, in_, out): # INVAR: (<op> in) = out) vars_ = [in_, out] comment = "" #(bvop.__name__ + " (in, out) = (%s, %s)")%(tuple([x.symbol_name() for x in vars_])) Logger.log(comment, 3) in_B = get_type(in_).is_bool_type() outB = get_type(out).is_bool_type() bools = (1 if in_B else 0) + (1 if outB else 0) if bop == None: if in_B: in_ = B2BV(in_) if outB: out = B2BV(out) invar = EqualsOrIff(bvop(in_), out) else: if bools == 2: invar = EqualsOrIff(bop(in_), out) elif bools == 0: invar = EqualsOrIff(bvop(in_), out) else: if not in_B: invar = EqualsOrIff(bop(BV2B(in_)), out) if not outB: invar = EqualsOrIff(bop(in_), BV2B(out)) ts = TS(comment) ts.vars, ts.invar = get_free_variables(invar), invar return ts
def all_loopbacks(self, vars, k, heqvar=None): lvars = list(vars) vars_k = [TS.get_timed(v, k) for v in lvars] loopback = FALSE() eqvar = None heqvars = None if heqvar is not None: eqvar = Symbol(EQVAR, BOOL) heqvars = [] peqvars = FALSE() for i in range(k): vars_i = [TS.get_timed(v, i) for v in lvars] eq_k_i = And( [EqualsOrIff(vars_k[j], vars_i[j]) for j in range(len(lvars))]) if heqvar is not None: eqvar_i = TS.get_timed(eqvar, i) peqvars = Or(peqvars, eqvar_i) eq_k_i = And(eqvar_i, Iff(eqvar_i, eq_k_i)) heqvars.append(Iff(TS.get_timed(heqvar, i), peqvars)) loopback = Or(loopback, eq_k_i) if heqvar is not None: loopback = And(loopback, And(heqvars)) return loopback
def MemAcc(self, left, right): primed = False if TS.is_prime(left): primed = True left = TS.get_ref_var(left) timed_symbol = lambda v: v if not primed else TS.get_prime(v) memname = left.symbol_name() allsymbols = list(get_env().formula_manager.get_all_symbols()) memsymbols = [(v.symbol_name(), timed_symbol(v)) for v in allsymbols \ if (not TS.is_prime(v)) and (not TS.is_timed(v)) and (v.symbol_name()[:len(memname)] == memname) \ and v.symbol_name() != memname] memsymbols.sort() memsize = len(memsymbols) if memsize < 1: Logger.error("Memory \"%s\" has size 1"%(memname)) if right.is_int_constant(): location = right.constant_value() if location > memsize-1: Logger.error("Out of bound access for memory \"%s\", size %d"%(memname, memsize)) return memsymbols[location][1] else: if not (right.is_symbol() and right.symbol_type().is_bv_type()): Logger.error("Symbolic memory access requires Bitvector indexing") width_idx = right.symbol_type().width width_mem = min(memsize, width_idx) return mem_access(right, [m[1] for m in memsymbols], width_idx)
def Posedge(self, x): if get_type(x).is_bool_type(): if (self.encoder_config is not None) and (self.encoder_config.abstract_clock): return x return And(Not(x), TS.to_next(x)) if (self.encoder_config is not None) and (self.encoder_config.abstract_clock): return EqualsOrIff(x, BV(1,1)) return And(EqualsOrIff(x, BV(0,1)), BV2B(TS.to_next(x)))
def Negedge(self, x): if get_type(x).is_bool_type(): if (self.encoder_config is not None) and (self.encoder_config.abstract_clock): return Not(x) return And(x, Not(TS.to_next(x))) if (self.encoder_config is not None) and (self.encoder_config.abstract_clock): return EqualsOrIff(x, BV(0,1)) return And(BV2B(x), EqualsOrIff(TS.to_next(x), BV(0,1)))
def Undriven(_out): ''' Undriven is a no-op. _out is just undriven ''' vars_ = [_out] ts = TS("Undriven wire") ts.vars = set(vars_) return ts
def _remap_model_zz(self, vars, model, k): retmodel = dict([el for el in dict(model).items() if not TS.is_ptimed(el[0])]) for var in vars: for t in range(int(k/2)+1, k+1, 1): retmodel[TS.get_timed(var, t)] = model[TS.get_ptimed(var, k-t)] return retmodel
def __encoding_memoization(self, inst_type, args): value = 0 vec_par = [] actual = [] for x in args: if x is not None: if type(x) != int: value += 1 vec_par.append(("a%s" % value, x.symbol_type().width)) actual.append(x) else: vec_par.append(x) def join(l1, l2): ret = [] for i in range(len(l1)): ret.append((l1[i], l2[i])) return ret def set_remap(in_set, remap): ret = set([]) source = False for v in in_set: if v in remap: ret.add(remap[v]) else: if source is False: base_source = ".".join( list(in_set)[0].symbol_name().split(".")[:-1]) base_dest = ".".join(remap[list( in_set)[0]].symbol_name().split(".")[:-1]) source = True ret.add( Symbol(v.symbol_name().replace(base_source, base_dest), v.symbol_type())) return ret enc_val = (inst_type, str(vec_par)) if enc_val in self.enc_map: (enc, formal) = self.enc_map[enc_val] remap = dict(join(formal, actual)) self.subwalker.set_substitute_map(remap) ts = TS() ts.vars = set_remap(enc.vars, remap) ts.set_behavior(self.subwalker.walk(enc.init), self.subwalker.walk(enc.trans), self.subwalker.walk(enc.invar)) return ts ret = self.mod_map[inst_type][0](*args) self.enc_map[enc_val] = (ret, actual) return ret
def _remap_model_bwd(self, vars, model, k): retmodel = dict() for var in vars: for t in range(k + 1): retmodel[TS.get_timed(var, t)] = model[TS.get_ptimed(var, k - t)] return retmodel
def Wrap(in_, out): # INVAR: (in = out) vars_ = [in_,out] comment = ("Wrap (in, out) = (%s, %s)")%(tuple([x.symbol_name() for x in vars_])) Logger.log(comment, 3) invar = EqualsOrIff(in_, out) ts = TS(comment) ts.vars, ts.invar = set(vars_), invar return ts
def _remap_model_zz(self, vars, model, k): retmodel = dict(model) for var in vars: for t in range(int(k / 2) + 1, k + 1, 1): retmodel[TS.get_timed(var, t)] = model[TS.get_ptimed(var, k - t)] return retmodel
def Slice(in_, out, low, high): # INVAR: (extract low high in) = out high -= 1 vars_ = [in_,out, low, high] comment = "Mux (in, out, low, high) = (%s, %s, %s, %s)"%(tuple([str(x) for x in vars_])) Logger.log(comment, 3) invar = EqualsOrIff(BVExtract(in_, low, high), out) ts = TS(comment) ts.vars, ts.invar = set([in_, out]), invar return ts
def revise_abstract_clock(model, abstract_clock_list): newmodel = {} abs_clock = dict(abstract_clock_list) length = 0 for var, value in model.items(): refvar = TS.get_ref_var(var) time = TS.get_time(var) if time > 0: if refvar not in abs_clock: newmodel[TS.get_timed(refvar, (time*2)-1)] = value newmodel[TS.get_timed(refvar, (time*2))] = value else: newmodel[TS.get_timed(refvar, (time*2)-1)] = abs_clock[refvar][1] if value == abs_clock[refvar][1]: newmodel[TS.get_timed(refvar, (time*2))] = abs_clock[refvar][0] else: newmodel[TS.get_timed(refvar, (time*2))] = abs_clock[refvar][1] if ((time*2)+1) > length: length = ((time*2)) else: if refvar not in abs_clock: newmodel[TS.get_timed(refvar, 0)] = value else: newmodel[TS.get_timed(refvar, 0)] = abs_clock[refvar][0] return (newmodel, length)
def extend_ts(ts, modifier): affect_init = False if ts.ftrans is None: return (ts, []) new_ftrans = {} vars = [] for (assign, cond_assign_list) in ts.ftrans.items(): fv = get_free_variables(assign) assert len(fv) == 1 var = fv.pop() is_next = TS.has_next(var) refvar = TS.get_ref_var(var) nomvar = Symbol(NOMIN % refvar.symbol_name(), var.symbol_type()) fvar = Symbol(FAULT % refvar.symbol_name(), BOOL) vars.append(nomvar) vars.append(fvar) repldic = dict([(refvar.symbol_name(), nomvar.symbol_name()), \ (TS.get_prime(refvar).symbol_name(), TS.get_prime(nomvar).symbol_name())]) # Remapping nominal behavior to new variable new_ftrans[substitute(assign, repldic)] = [(substitute(c[0], repldic), substitute(c[1], repldic)) for c in cond_assign_list] # Definition of the nominal behavior new_ftrans[refvar] = [(Not(fvar), nomvar)] # Application of the faulty behavior new_ftrans[refvar].append( (fvar, modifier.get_behavior(nomvar, refvar))) ts.trans = And(ts.trans, Implies(fvar, TS.get_prime(fvar))) if affect_init: ts.init = substitute(ts.init, repldic) else: ts.init = And(ts.init, Not(fvar)) # add the vars to the transition system for var in vars: ts.add_var(var) ts.ftrans = new_ftrans return (ts, vars)
def Andr(in_, out): # INVAR: (in = 2**width - 1) -> (out = 1) & (in != 2**width - 1) -> (out = 0) vars_ = [in_, out] comment = "Andr (in, out) = (%s, %s)"%(tuple([x.symbol_name() for x in vars_])) Logger.log(comment, 3) width = in_.symbol_type().width eq_all_ones = EqualsOrIff(in_, BV(2**width - 1,width)) true_res = Implies(eq_all_ones, EqualsOrIff(out, BV(1,1))) false_res = Implies(Not(eq_all_ones), EqualsOrIff(out, BV(0,1))) invar = And(true_res, false_res) ts = TS(comment) ts.vars, ts.invar = set(vars_), invar return ts
def MultiOp(op, end_op, out, *inparams): cum = inparams[0] for el in inparams[1:]: cum = op(cum, el) if end_op is not None: cum = end_op(cum) formula = EqualsOrIff(cum, out) ts = TS() ts.vars, ts.invar = get_free_variables(formula), formula return ts
def Const(out, value): invar = TRUE() if value is not None: if out.symbol_type() == BOOL: const = TRUE() if value == 1 else FALSE() else: const = BV(value, out.symbol_type().width) invar = EqualsOrIff(out, const) comment = "Const (out, val) = (" + out.symbol_name() + ", " + str(value) + ")" Logger.log(comment, 3) ts = TS(comment) ts.vars, ts.invar = set([out]), invar return ts
def Clock(clk): # INIT: clk = 0 # TRANS: clk' = !clk comment = "Clock (clk) = (" + clk.symbol_name() + ")" if clk.symbol_type() == BOOL: clk0 = Not(clk) clk1 = clk else: clk0 = EqualsOrIff(clk, BV(0, 1)) clk1 = EqualsOrIff(clk, BV(1, 1)) init = clk0 invar = TRUE() if False: trans = EqualsOrIff(clk0, TS.to_next(clk1)) else: # Implementation that leverages on the boolean propagation trans1 = Implies(clk0, TS.to_next(clk1)) trans2 = Implies(clk1, TS.to_next(clk0)) trans = And(trans1, trans2) if Modules.abstract_clock: invar = clk0 init = TRUE() trans = TRUE() ts = TS(comment) ts.vars, ts.state_vars = set([clk]), set([clk]) ts.set_behavior(init, trans, invar) return ts
def BopBool(op, in0, in1, out): # INVAR: (in0 <op> in1) = out vars_ = [in0,in1,out] comment = (op.__name__ + " (in0, in1, out) = (%s, %s, %s)")%(tuple([x.symbol_name() for x in vars_])) Logger.log(comment, 3) if out.symbol_type() == BOOL: bout = out else: bout = EqualsOrIff(out, BV(1, 1)) invar = Iff(op(in0,in1), bout) ts = TS(comment) ts.vars, ts.invar = get_free_variables(invar), invar return ts
def parse_formulae(self, strforms): formulae = [] if strforms is None: return formulae for strform in strforms: if ("#" not in strform) and (strform != ""): formula = self.parse_formula(strform) formula_fv = get_free_variables(formula) nextvars = [v for v in formula_fv if TS.is_prime(v)] != [] prevvars = [v for v in formula_fv if TS.is_prev(v)] != [] formulae.append((strform, formula, (nextvars, prevvars))) return formulae
def _get_param_assignments(self, model, time, parameters, monotonic=True): p_ass = [] fwd = False for p in parameters: # search the trace for any enabled faults ever_true = False for t in range(time + 1): p_time = model[TS.get_ptimed(p, 0)] if p.symbol_type() == BOOL: if p_time == TRUE(): ever_true = True break if ever_true: p_ass.append(p) elif not monotonic: p_ass.append(EqualsOrIff(p, FALSE())) p_ass = And(p_ass) self.region = simplify(Or(self.region, p_ass)) if self.models is None: self.models = [] self.models.append((model, time)) Logger.msg("+", 0, not (Logger.level(1))) self.cs_count += 1 Logger.log( "Found assignment \"%s\"" % (p_ass.serialize(threshold=100)), 1) return (p_ass, False)
def solve_safety_inc_zz(self, hts, prop, k): self._reset_assertions(self.solver) if TS.has_next(prop): Logger.error( "Invariant checking with next variables only supports FWD strategy" ) init = hts.single_init() trans = hts.single_trans() invar = hts.single_invar() initt = self.at_time(And(init, invar), 0) Logger.log("Add init at_0", 2) self._add_assertion(self.solver, initt) propt = self.at_ptime(And(Not(prop), invar), -1) Logger.log("Add property pat_%d" % 0, 2) self._add_assertion(self.solver, propt) t = 0 while (t < k + 1): self._push(self.solver) even = (t % 2) == 0 th = int(t / 2) if even: eq = And([ EqualsOrIff(self.at_time(v, th), self.at_ptime(v, th - 1)) for v in hts.vars ]) else: eq = And([ EqualsOrIff(self.at_time(v, th + 1), self.at_ptime(v, th - 1)) for v in hts.vars ]) Logger.log("Add equivalence time %d" % t, 2) self._add_assertion(self.solver, eq) if self._solve(self.solver): Logger.log("Counterexample found with k=%s" % (t), 1) model = self._get_model(self.solver) return (t, model) else: Logger.log("No counterexample found with k=%s" % (t), 1) Logger.msg(".", 0, not (Logger.level(1))) self._pop(self.solver) if even: trans_t = self.unroll(trans, invar, th + 1, th) else: trans_t = self.unroll(trans, invar, th, th + 1) self._add_assertion(self.solver, trans_t) t += 1 return (t - 1, None)
def _get_param_assignments(self, model, time, parameters, monotonic=True): p_ass = [] fwd = False for p in parameters: p_time = model[TS.get_ptimed(p, 0)] if p.symbol_type() == BOOL: if monotonic: if p_time == TRUE(): p_ass.append(p) else: p_ass.append(p if p_time == TRUE() else Not(p)) else: p_ass.append(EqualsOrIff(p, p_time)) p_ass = And(p_ass) self.region = simplify(Or(self.region, p_ass)) if self.models is None: self.models = [] self.models.append((model, time)) Logger.msg("+", 0, not (Logger.level(1))) self.cs_count += 1 Logger.log( "Found assignment \"%s\"" % (p_ass.serialize(threshold=100)), 1) return (p_ass, False)
def Term(_in): ''' Term is a no-op. Just terminates a coreir wireable ''' vars_ = [_in] ts = TS("Terminate wire") return ts
def get_sts(self, params): if len(params) != len(self.interface.split()): Logger.error("Invalid parameters for clock behavior \"%s\"" % (self.name)) clk = params[0] valuepar = params[1] if (not type(clk) == FNode) or (not clk.is_symbol()): Logger.error("Clock symbol \"%s\" not found" % (str(clk))) if (type(valuepar) == FNode) and (valuepar.is_bv_constant()): value = valuepar.constant_value() else: try: value = int(valuepar) except: Logger.error( "Clock value should be an integer number instead of \"%s\"" % valuepar) init = [] invar = [] trans = [] vars = set([]) if clk.symbol_type().is_bv_type(): pos_clk = EqualsOrIff(clk, BV(1, 1)) neg_clk = EqualsOrIff(clk, BV(0, 1)) else: pos_clk = clk neg_clk = Not(clk) if value == 1: invar.append(pos_clk) else: invar.append(neg_clk) ts = TS("Clock Behavior") ts.vars, ts.init, ts.invar, ts.trans = vars, And(init), And( invar), And(trans) Logger.log( "Adding clock behavior \"%s(%s)\"" % (self.name, ", ".join([str(p) for p in params])), 1) return ts