#!/usr/bin/env python3 class FNode: def __init__(self, fun, *args): self.fun = fun self.args = args if len(self.args) == 0: assert fun not in ("BUF", "NOT", "AND", "OR", "XOR", "MUX") if len(self.args) == 1: assert fun in ("BUF", "NOT") if len(self.args) == 2: assert fun in ("AND", "OR", "XOR") if len(self.args) == 3: assert fun in ("MUX") def __str__(self): if len(self.args) == 0: return self.fun if self.fun == "NOT" and len(self.args[0].args) == 0: return "!" + self.args[0].fun return self.fun + "(" + ",".join([str(a) for a in self.args]) + ")" def as_genlib_term(self): if len(self.args) == 0: return self.fun if self.fun == "NOT": assert len(self.args[0].args) == 0 return "!" + self.args[0].fun if self.fun == "AND": return "(" + self.args[0].as_genlib_term() + "*" + self.args[1].as_genlib_term() + ")" if self.fun == "OR": return "(" + self.args[0].as_genlib_term() + "+" + self.args[1].as_genlib_term() + ")" assert False def mapMux(self): if self.fun == "MUX": A, B, C = self.args return OR(AND(A, NOT(C)), AND(B, C)).mapMux() return FNode(self.fun, *[a.mapMux() for a in self.args]) def mapXor(self): if self.fun == "XOR": A, B = self.args return OR(AND(A, NOT(B)), AND(NOT(A), B)).mapXor() return FNode(self.fun, *[a.mapXor() for a in self.args]) def mapNot(self): if self.fun == "BUF": return self.arg1.mapNot() if self.fun == "NOT": if self.args[0].fun == "AND": return OR(NOT(self.args[0].args[0]),NOT(self.args[0].args[1])).mapNot() if self.args[0].fun == "OR": return AND(NOT(self.args[0].args[0]),NOT(self.args[0].args[1])).mapNot() if self.args[0].fun == "NOT": return self.args[0].args[0].mapNot() return FNode(self.fun, *[a.mapNot() for a in self.args]) def map(self): n = self n = n.mapMux() n = n.mapXor() n = n.mapNot() return n def isInv(self): if len(self.args) == 0: return False if self.fun == "XOR": return False if self.fun == "NOT": return self.args[0].isNonInv() for a in self.args: if not a.isInv(): return False return True def isNonInv(self): if len(self.args) == 0: return True if self.fun == "XOR": return False if self.fun == "NOT": return self.args[0].isInv() for a in self.args: if not a.isNonInv(): return False return True A = FNode("A") B = FNode("B") C = FNode("C") D = FNode("D") E = FNode("E") def BUF(arg): return FNode("BUF", arg) def NOT(arg): return FNode("NOT", arg) def AND(arg1, arg2): return FNode("AND", arg1, arg2) def OR(arg1, arg2): return FNode( "OR", arg1, arg2) def XOR(arg1, arg2): return FNode("XOR", arg1, arg2) def MUX(arg1, arg2, arg3): return FNode("MUX", arg1, arg2, arg3) # Genlib Format: # # GATE # # PIN # # # # phase: # INV, NONINV, or UNKNOWN # # cell-logic-function: # = cells = [ ["$__CC_BUF", 5, A], ["$__CC_NOT", 0, NOT(A)], ["$__CC_MUX", 5, MUX(A, B, C)], ] base_cells = [ ["$__CC2_A", AND(A, B)], ["$__CC2_O", OR(A, B)], ["$__CC2_X", XOR(A, B)], ["$__CC3_AA", AND(AND(A, B), C)], ["$__CC3_OO", OR( OR(A, B), C)], ["$__CC3_XX", XOR(XOR(A, B), C)], ["$__CC3_AO", OR(AND(A, B), C)], ["$__CC3_OA", AND( OR(A, B), C)], ["$__CC3_AX", XOR(AND(A, B), C)], ["$__CC3_XA", AND(XOR(A, B), C)], # ["$__CC3_AAA", AND(AND(A,B),AND(A,C))], # ["$__CC3_AXA", XOR(AND(A,B),AND(A,C))], # ["$__CC3_XAX", AND(XOR(A,B),XOR(A,C))], # ["$__CC3_AAX", AND(AND(A,B),XOR(A,C))], # ["$__CC3_AXX", XOR(AND(A,B),XOR(A,C))], # ["$__CC3_XXX", XOR(XOR(A,B),XOR(A,C))], # ["$__CC3_AAO", AND(AND(A,B), OR(A,C))], # ["$__CC3_AOA", OR(AND(A,B),AND(A,C))], # ["$__CC3_AOX", OR(AND(A,B),XOR(A,C))], # ["$__CC3_AAA_N", AND(AND(A,B),AND(NOT(A),C))], # ["$__CC3_AXA_N", XOR(AND(A,B),AND(NOT(A),C))], # ["$__CC3_XAX_N", AND(XOR(A,B),XOR(NOT(A),C))], # ["$__CC3_AAX_N", AND(AND(A,B),XOR(NOT(A),C))], # ["$__CC3_AXX_N", XOR(AND(A,B),XOR(NOT(A),C))], # ["$__CC3_XXX_N", XOR(XOR(A,B),XOR(NOT(A),C))], # ["$__CC3_AAO_N", AND(AND(A,B), OR(NOT(A),C))], # ["$__CC3_AOA_N", OR(AND(A,B),AND(NOT(A),C))], # ["$__CC3_AOX_N", OR(AND(A,B),XOR(NOT(A),C))], ["$__CC4_AAA", AND(AND(A,B),AND(C,D))], ["$__CC4_AXA", XOR(AND(A,B),AND(C,D))], ["$__CC4_XAX", AND(XOR(A,B),XOR(C,D))], ["$__CC4_AAX", AND(AND(A,B),XOR(C,D))], ["$__CC4_AXX", XOR(AND(A,B),XOR(C,D))], ["$__CC4_XXX", XOR(XOR(A,B),XOR(C,D))], ["$__CC4_AAO", AND(AND(A,B), OR(C,D))], ["$__CC4_AOA", OR(AND(A,B),AND(C,D))], ["$__CC4_AOX", OR(AND(A,B),XOR(C,D))], ] for name, expr in base_cells: cells.append([name, 10, expr]) name = (name .replace("$__CC4_", "$__CC5_") .replace("$__CC3_", "$__CC4_") .replace("$__CC2_", "$__CC3_")) # Cells such as $__CC4_AA_A are redundant, as $__CC4_AAA is equivalent if name not in ("$__CC4_AA", "$__CC3_A"): cells.append([name + "_A", 12, AND(E, expr)]) if name not in ("$__CC4_OO", "$__CC3_O"): cells.append([name + "_O", 12, OR(E, expr)]) if name not in ("$__CC4_XX", "$__CC3_X"): cells.append([name + "_X", 12, XOR(E, expr)]) with open("techlibs/gatemate/lut_tree_cells.genlib", "w") as glf: def mkGate(name, cost, expr, max_load=9999, block_delay = 10, fanout_delay = 5): name = name.replace(" ", "") expr = expr.map() phase = "UNKNOWN" if expr.isInv(): phase = "INV" if expr.isNonInv(): phase = "NONINV" print("", file=glf) print("GATE %s %d Y=%s;" % (name, cost, expr.as_genlib_term()), file=glf) print("PIN * %s 1 %d %d %d %d %d" % (phase, max_load, block_delay, fanout_delay, block_delay, fanout_delay), file=glf) print("GATE $__ZERO 0 Y=CONST0;", file=glf) print("GATE $__ONE 0 Y=CONST1;", file=glf) for name, cost, expr in cells: mkGate(name, cost, expr) class LUTTreeNode: def __init__(self, name, width, inputs=None): self.name = name self.width = width self.inputs = inputs def is_input(self): return self.width == 0 def map(self, expr, params, ports): if self.is_input(): # Input to LUT tree if expr is None: ports[self.name] = "" # disconnected input else: assert(len(expr.args) == 0) ports[self.name] = expr.fun return if expr is None: # Unused part of tree params[self.name] = "4'b0000" for i in self.inputs: i.map(None, params, ports) return elif len(expr.args) == 0: # Input to the expression; but not LUT tree # Insert a route through params[self.name] = "4'b1010" self.inputs[0].map(expr, params, ports) for i in self.inputs[1:]: i.map(None, params, ports) return # Map uphill LUTs; uninverting arguments and keeping track of that if needed arg_inv = [] for (i, arg) in zip(self.inputs, expr.args): if arg.fun == "NOT": i.map(arg.args[0], params, ports) arg_inv.append(True) else: i.map(arg, params, ports) arg_inv.append(False) # Determine base init value assert self.width == 2 if expr.fun == "AND": init = 0b1000 elif expr.fun == "OR": init = 0b1110 elif expr.fun == "XOR": init = 0b0110 else: assert False, expr.fun # Swap bits if init inverted swapped_init = 0b0000 for b in range(4): if ((init >> b) & 0x1) == 0: continue for i in range(2): if arg_inv[i]: b ^= (1 << i) swapped_init |= (1 << b) # Set init param params[self.name] = "4'b{:04b}".format(swapped_init) def LUT2(name, i0, i1): return LUTTreeNode(name, 2, [i0, i1]) def I(name): return LUTTreeNode(name, 0) lut_prims = { "CC_LUT2": LUT2("INIT", I("I0"), I("I1")), "CC_L2T4": LUT2( "INIT_L10", LUT2("INIT_L00", I("I0"), I("I1")), LUT2("INIT_L01", I("I2"), I("I3")), ), "CC_L2T5": LUT2( "INIT_L20", I("I4"), LUT2("INIT_L11", LUT2("INIT_L02", I("I0"), I("I1")), LUT2("INIT_L03", I("I2"), I("I3")), ) ) } with open("techlibs/gatemate/lut_tree_map.v", "w") as vf: # Non-automatic rules print(""" module \\$__ZERO (output Y); assign Y = 1'b0; endmodule module \\$__ONE (output Y); assign Y = 1'b1; endmodule module \\$__CC_BUF (input A, output Y); assign Y = A; endmodule module \\$__CC_MUX (input A, B, C, output Y); CC_MX2 _TECHMAP_REPLACE_ ( .D0(A), .D1(B), .S0(C), .Y(Y) ); endmodule """, file=vf) for name, cost, expr in cells: expr = expr.mapMux().mapNot() # Don't map XOR if name in ("$__CC_BUF", "$__CC_NOT", "$__CC_MUX"): # Special cases continue if name.startswith("$__CC2_"): prim = "CC_LUT2" elif name.startswith("$__CC5_") or (name.startswith("$__CC4_") and cost == 12): prim = "CC_L2T5" else: prim = "CC_L2T4" ports = {} params = {} lut_prims[prim].map(expr, params, ports) print("", file=vf) print("module \\%s (input %s, output Y);" % (name, ", ".join(sorted(set(x for x in ports.values() if x)))), file=vf) print(" %s #(" % prim, file=vf) for k, v in sorted(params.items(), key=lambda x: x[0]): print(" .%s(%s)," % (k, v), file=vf) print(" ) _TECHMAP_REPLACE_ (", file=vf) print(" %s," % ", ".join(".%s(%s)" % (k, v) for k, v in sorted(ports.items(), key=lambda x:x[0])), file=vf) print(" .O(Y)", file=vf) print(" );", file=vf) print("endmodule", file=vf)