def init_routing_vars(cgra : MRRG, design : Design, vars : Modeler, solver : Solver) -> Term: bv1 = solver.BitVec(1) for node in cgra.all_nodes: for value in design.values: vars.init_var((node, value), bv1) for dst in value.dsts: vars.init_var((node, value, dst), bv1) return solver.TheoryConst(solver.Bool(), True)
def pe_exclusivity(cgra : MRRG, design : Design, vars : Modeler, solver : Solver) -> Term: ''' Assert all PEs are used at most one time ''' bv = solver.BitVec(len(design.operations)) c = [] for pe in cgra.functional_units: pe_vars = vars.anonymous_var(bv) for idx, op in enumerate(design.operations): c.append(pe_vars[idx] == vars[pe, op]) c.append(_is_one_hot_or_0(pe_vars, solver)) return solver.And(c)
def op_placement(cgra : MRRG, design : Design, vars : Modeler, solver : Solver) -> Term: ''' Assert all ops are placed exactly one time unless they can be duplicated in which case assert they are placed ''' bv = solver.BitVec(len(cgra.functional_units)) c = [] for op in design.operations: if op.duplicate: c.append(ft.reduce(solver.BVOr, (vars[pe, op] for pe in cgra.functional_units)) == 1) else: op_vars = vars.anonymous_var(bv) for idx,pe in enumerate(cgra.functional_units): c.append(op_vars[idx] == vars[pe, op]) c.append(_is_one_hot(op_vars, solver)) return solver.And(c)
def route_exclusivity(cgra : MRRG, design : Design, vars : Modeler, solver : Solver) -> Term: ''' each routing node is used for at most one value for all node in nodes: popcount(vars[node, value] for value in values) <= 1 ''' bv = solver.BitVec(len(design.values)) c = [] for node in cgra.all_nodes: node_vars = vars.anonymous_var(bv) for idx, value in enumerate(design.values): c.append(node_vars[idx] == vars[node, value]) c.append(_is_one_hot_or_0(node_vars, solver)) return solver.And(c)
def pe_legality(cgra : MRRG, design : Design, vars : Modeler, solver : Solver) -> Term: ''' Assert ops are not placed on PE's that do not support them ''' c = [] for pe in cgra.functional_units: for op in design.operations: if op.opcode not in pe.ops: c.append(vars[pe, op] == 0) return solver.And(c)
def limit_popcount_total( node_filter : NodeFilter, l : int, n : int, cgra : MRRG, design : Design, vars : Modeler, solver : Solver) -> Term: v = vars[node_filter] if n is None: return solver.BVUle(l, v) if n < 0 or n < l: assert 0 elif n.bit_length() > v.sort.width or l.bit_length() > v.sort.width: assert 0 else: # return solver.BVUle(v, n) return solver.And(solver.BVUle(l, v), solver.BVUle(v, n))
def output_connectivity(cgra : MRRG, design : Design, vars : Modeler, solver : Solver) -> Term: ''' if node used to route a value then exactly one of its outputs also routes that value ''' c = [] for node in cgra.all_nodes: bv = solver.BitVec(len(node.outputs.values())) for value in design.values: if isinstance(node, mrrg.FU_Port): continue for dst in value.dsts: v = vars[node, value, dst] i_vars = vars.anonymous_var(bv) for idx, n in enumerate(node.outputs.values()): c.append(i_vars[idx] == vars[n, value, dst]) c.append(solver.Or(v == 0, _is_one_hot(i_vars, solver))) return solver.And(c)
def init_popcount_concat( node_filter : NodeFilter, cgra : MRRG, design : Design, vars : Modeler, solver : Solver) -> Term: nodes = [n for n in cgra.all_nodes if node_filter(n)] width = len(nodes).bit_length() zero = solver.TheoryConst(solver.BitVec(width - 1), 0) zeroExt = ft.partial(solver.Concat, zero) expr = ft.reduce(solver.BVAdd, map(zeroExt, (ft.reduce(solver.BVOr, (vars[n, v] for v in design.values) ) for n in nodes) ) ) pop_count = vars.init_var(node_filter, expr.sort) return expr == pop_count
def freaze_fus( model : Model, cgra : MRRG, design : Design, vars : Modeler, solver : Solver) -> Term: c = [] for pe in cgra.functional_units: for op in design.operations: c.append(vars[pe, op] == model[pe, op]) return solver.And(c)
def routing_resource_usage(cgra : MRRG, design : Design, vars : Modeler, solver : Solver) -> Term: ''' if a routing node is used to route a value to a dst then then it is used to route the value ''' c = [] for node in cgra.all_nodes: for value in design.values: v = vars[node, value] v_ = ft.reduce(solver.BVOr, (vars[node, value, dst] for dst in value.dsts)) c.append(v == v_) return solver.And(c)
def init_popcount_ite( node_filter : NodeFilter, cgra : MRRG, design : Design, vars : Modeler, solver : Solver) -> Term: nodes = [n for n in cgra.all_nodes if node_filter(n)] bv = solver.BitVec(len(nodes).bit_length()) zero = solver.TheoryConst(bv, 0) one = solver.TheoryConst(bv, 1) expr = ft.reduce(solver.BVAdd, map(lambda x : solver.Ite(x == 0, zero, one), (ft.reduce(solver.BVOr, (vars[n, v] for v in design.values) ) for n in nodes) ) ) pop_count = vars.init_var(node_filter, bv) return expr == pop_count
def port_placement(cgra : MRRG, design : Design, vars : Modeler, solver : Solver) -> Term: ''' values terminate at the input port of their op ''' c = [] for pe in cgra.functional_units: for value in design.values: for dst in value.dsts: op, operand = dst if op.opcode not in pe.ops: for port in pe.operands.values(): v_ = vars[port, value, dst] c.append(v_ == 0) else: port = pe.operands[operand] v = vars[pe, op] v_ = vars[port, value, dst] c.append(v == v_) return solver.And(c)
def init_value(cgra : MRRG, design : Design, vars : Modeler, solver : Solver) -> Term: ''' values are routed from the pe which holds their source op ''' c = [] for pe in cgra.functional_units: for value in design.values: src = value.src v = vars[pe, src] if src.opcode not in pe.ops: c.append(vars[pe, value] == 0) elif src.duplicate: v_ = vars[pe, value] c.append(v == v_) else: for dst in value.dsts: v_ = vars[pe, value, dst] c.append(v == v_) return solver.And(c)
def init_popcount_bithack( node_filter : NodeFilter, cgra : MRRG, design : Design, vars : Modeler, solver : Solver) -> Term: def _build_grouped_mask(k, n): ''' build_grouped_mask :: int -> int -> Term returns the unique int m of length n that matches the following RE ((0{0,k} 1{k}) | (1{0,k})) (0{k} 1{k})* ''' m = 0 for i in range(k): m |= 1 << i c = 2*k while c < n: m |= m << c c *= 2 return solver.TheoryConst(solver.BitVec(n), m) def _is_power_of_2(x : int) -> bool: return x & (x - 1) == 0 def _floor_log2(x : int) -> int: return x.bit_length() - 1 def _prev_power_of_2(x : int) -> int: return 1 << _floor_log2(x - 1) def _next_power_of_2(x : int) -> int: return 1 << x.bit_length() constraints = [] vs = [vars[n, v] for n in cgra.all_nodes if node_filter(n) for v in design.values] width = len(vs) # build a bitvector from the concanation of bits bv = vars.anonymous_var(solver.BitVec(width)) for idx,v in enumerate(vs): constraints.append(bv[idx] == v) # Boolector can't handle lshr on non power of 2, so zero extend if solver.solver_name == 'Boolector' and not _is_power_of_2(width): l = _next_power_of_2(width) bv = solver.Concat(solver.TheoryConst(solver.BitVec(l - width), 0), bv) width = bv.sort.width pop_count = vars.init_var(node_filter, bv.sort) if width <= 1: constraints.append(pop_count == bv) return solver.And(constraints) elif width == 2: constraints.append(pop_count == (bv & 1) + (bv >> 1)) return solver.And(constraints) max_shift = _prev_power_of_2(width) def _mask_shift_add(x, shift): mask = _build_grouped_mask(shift, width) return (x & mask) + ((x >> shift) & mask) shifts = it.takewhile(lambda n : n <= max_shift, (1 << i for i in it.count())) x = ft.reduce(_mask_shift_add, shifts, bv) constraints.append(pop_count == x) return solver.And(constraints)
def _is_one_hot(var : Term, solver : Solver) -> Term: return solver.And(_is_one_hot_or_0(var, solver), var != solver.TheoryConst(var.sort, 0))
def _is_one_hot_or_0(var : Term, solver : Solver): return (var & (var - 1)) == solver.TheoryConst(var.sort, 0)
def init_placement_vars(cgra : MRRG, design : Design, vars : Modeler, solver : Solver) -> Term: bv1 = solver.BitVec(1) for pe in cgra.functional_units: for op in design.operations: vars.init_var((pe, op), bv1) return solver.TheoryConst(solver.Bool(), True)