#!/usr/bin/env python3 from z3 import * import sys I_AND, I_OR, I_XOR, I_NOT, I_NOR3, I_NAND, I_ANDN = 0,1,2,3,4,5,6 # 2-input function BITS=4 add_always_false=False add_always_true=False #add_always_false=True #add_always_true=True avail=[I_NAND] #avail=[I_ANDN] MAX_STEPS=10 # My representation of TT is: [MSB..LSB]. # Knuth's representation in the section 7.1.1 (Boolean basics) of TAOCP is different: [LSB..MSB]. # so I'll reverse bits before printing TT: def rvr_4_bits(i): return ((i>>0)&1)<<3 | ((i>>1)&1)<<2 | ((i>>2)&1)<<1 | ((i>>3)&1)<<0 def find_NAND_only_for_TT (OUTPUTS): INPUTS=[0b1100, 0b1010] # if additional always-false or always-true must be present: if add_always_false: INPUTS.append(0) if add_always_true: INPUTS.append(2**BITS-1) # this called during self-testing: def eval_ins(R, s, m, STEPS, op, op1_reg, op2_reg, op3_reg): op_n=m[op[s]].as_long() op1_reg_tmp=m[op1_reg[s]].as_long() op1_val=R[op1_reg_tmp] op2_reg_tmp=m[op2_reg[s]].as_long() op3_reg_tmp=m[op3_reg[s]].as_long() if op_n in [I_AND, I_OR, I_XOR, I_NOR3, I_NAND, I_ANDN]: op2_val=R[op2_reg_tmp] if op_n==I_AND: return op1_val&op2_val elif op_n==I_OR: return op1_val|op2_val elif op_n==I_XOR: return op1_val^op2_val elif op_n==I_NOT: return ~op1_val elif op_n==I_NOR3: op3_val=R[op3_reg_tmp] return ~(op1_val|op2_val|op3_val) elif op_n==I_NAND: return ~(op1_val&op2_val) elif op_n==I_ANDN: return (~op1_val)&op2_val else: raise AssertionError # evaluate program we've got. for self-testing. def eval_pgm(m, STEPS, op, op1_reg, op2_reg, op3_reg): R=[None]*STEPS for i in range(len(INPUTS)): R[i]=INPUTS[i] for s in range(len(INPUTS),STEPS): R[s]=eval_ins(R, s, m, STEPS, op, op1_reg, op2_reg, op3_reg) return R # get all states, for self-testing: def selftest(m, STEPS, op, op1_reg, op2_reg, op3_reg): l=eval_pgm(m, STEPS, op, op1_reg, op2_reg, op3_reg) print ("simulate:") for i in range(len(l)): print ("r%d=" % i, format(l[i] & 2**BITS-1, '0'+str(BITS)+'b')) """ selector() function generates expression like: If(op1_reg_s5 == 0, S_s0, If(op1_reg_s5 == 1, S_s1, If(op1_reg_s5 == 2, S_s2, If(op1_reg_s5 == 3, S_s3, If(op1_reg_s5 == 4, S_s4, If(op1_reg_s5 == 5, S_s5, If(op1_reg_s5 == 6, S_s6, If(op1_reg_s5 == 7, S_s7, If(op1_reg_s5 == 8, S_s8, If(op1_reg_s5 == 9, S_s9, If(op1_reg_s5 == 10, S_s10, If(op1_reg_s5 == 11, S_s11, 0)))))))))))) this is like multiplexer or switch() """ def selector(R, s): t=0 # default value for i in range(MAX_STEPS): t=If(s==(MAX_STEPS-i-1), R[MAX_STEPS-i-1], t) return t def simulate_op(R, op, op1_reg, op2_reg, op3_reg): op1_val=selector(R, op1_reg) return If(op==I_AND, op1_val & selector(R, op2_reg), If(op==I_OR, op1_val | selector(R, op2_reg), If(op==I_XOR, op1_val ^ selector(R, op2_reg), If(op==I_NOT, ~op1_val, If(op==I_NOR3, ~(op1_val | selector(R, op2_reg) | selector (R, op3_reg)), If(op==I_NAND, ~(op1_val & selector(R, op2_reg)), If(op==I_ANDN, (~op1_val) & selector(R, op2_reg), 0))))))) # default op_to_str_tbl=["AND", "OR", "XOR", "NOT", "NOR3", "NAND", "ANDN"] def print_model(m, R, STEPS, op, op1_reg, op2_reg, op3_reg): print ("%d instructions for OUTPUTS TT (Knuth's representation)=" % (STEPS-len(INPUTS))), format(rvr_4_bits(OUTPUTS[0]) & 2**BITS-1, '0'+str(BITS)+'b') for s in range(STEPS): if s