mirror of https://github.com/YosysHQ/yosys.git
gatemate: Add LUT tree library script
Co-authored-by: Claire Xenia Wolf <claire@clairexen.net> Signed-off-by: gatecat <gatecat@ds0.me>
This commit is contained in:
parent
7c756c9959
commit
38a24ec5cc
|
@ -0,0 +1,4 @@
|
|||
lut_tree_cells.genlib
|
||||
lut_tree_map.v
|
||||
lut_tree_lib.mk
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
|
||||
OBJS += techlibs/gatemate/synth_gatemate.o
|
||||
OBJS += techlibs/gatemate/gatemate_foldinv.o
|
||||
|
||||
$(eval $(call add_share_file,share/gatemate,techlibs/gatemate/reg_map.v))
|
||||
$(eval $(call add_share_file,share/gatemate,techlibs/gatemate/mux_map.v))
|
||||
|
@ -12,3 +13,18 @@ $(eval $(call add_share_file,share/gatemate,techlibs/gatemate/brams_map.v))
|
|||
$(eval $(call add_share_file,share/gatemate,techlibs/gatemate/brams.txt))
|
||||
$(eval $(call add_share_file,share/gatemate,techlibs/gatemate/brams_init_20.vh))
|
||||
$(eval $(call add_share_file,share/gatemate,techlibs/gatemate/brams_init_40.vh))
|
||||
$(eval $(call add_share_file,share/gatemate,techlibs/gatemate/inv_map.v))
|
||||
|
||||
EXTRA_OBJS += techlibs/gatemate/lut_tree_lib.mk
|
||||
.SECONDARY: techlibs/gatemate/lut_tree_lib.mk
|
||||
|
||||
techlibs/gatemate/lut_tree_lib.mk: techlibs/gatemate/make_lut_tree_lib.py
|
||||
$(Q) mkdir -p techlibs/gatemate
|
||||
$(P) $(PYTHON_EXECUTABLE) $<
|
||||
$(Q) touch $@
|
||||
|
||||
techlibs/gatemate/lut_tree_cells.genlib: techlibs/gatemate/lut_tree_lib.mk
|
||||
techlibs/gatemate/lut_tree_map.v: techlibs/gatemate/lut_tree_lib.mk
|
||||
|
||||
$(eval $(call add_gen_share_file,share/gatemate,techlibs/gatemate/lut_tree_cells.genlib))
|
||||
$(eval $(call add_gen_share_file,share/gatemate,techlibs/gatemate/lut_tree_map.v))
|
||||
|
|
|
@ -0,0 +1,218 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2021 gatecat <gatecat@ds0.me>
|
||||
* Copyright (C) 2021 Cologne Chip AG <support@colognechip.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/sigtools.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
struct LUTPin {
|
||||
int input_bit;
|
||||
IdString init_param;
|
||||
};
|
||||
|
||||
struct LUTType {
|
||||
dict<IdString, LUTPin> inputs;
|
||||
IdString output_param;
|
||||
};
|
||||
|
||||
static const dict<IdString, LUTType> lut_types = {
|
||||
{ID(CC_LUT2), {{
|
||||
{ID(I0), {0, ID(INIT)}},
|
||||
{ID(I1), {1, ID(INIT)}},
|
||||
}, ID(INIT)}},
|
||||
{ID(CC_L2T4), {{
|
||||
{ID(I0), {0, ID(INIT_L00)}},
|
||||
{ID(I1), {1, ID(INIT_L00)}},
|
||||
{ID(I2), {0, ID(INIT_L01)}},
|
||||
{ID(I3), {1, ID(INIT_L01)}},
|
||||
}, ID(INIT_L10)}},
|
||||
{ID(CC_L2T5), {{
|
||||
{ID(I0), {0, ID(INIT_L02)}},
|
||||
{ID(I1), {1, ID(INIT_L02)}},
|
||||
{ID(I2), {0, ID(INIT_L03)}},
|
||||
{ID(I3), {1, ID(INIT_L03)}},
|
||||
{ID(I4), {0, ID(INIT_L20)}},
|
||||
}, ID(INIT_L20)}},
|
||||
};
|
||||
|
||||
struct FoldInvWorker {
|
||||
FoldInvWorker(Module *module) : module(module), sigmap(module) {};
|
||||
Module *module;
|
||||
SigMap sigmap;
|
||||
|
||||
// Mapping from inverter output to inverter input
|
||||
dict<SigBit, SigBit> inverted_bits;
|
||||
// Mapping from inverter input to inverter
|
||||
dict<SigBit, Cell*> inverter_input;
|
||||
|
||||
void find_inverted_bits()
|
||||
{
|
||||
for (auto cell : module->selected_cells()) {
|
||||
if (cell->type != ID($__CC_NOT))
|
||||
continue;
|
||||
SigBit a = sigmap(cell->getPort(ID::A)[0]);
|
||||
SigBit y = sigmap(cell->getPort(ID::Y)[0]);
|
||||
inverted_bits[y] = a;
|
||||
inverter_input[a] = cell;
|
||||
}
|
||||
}
|
||||
|
||||
Const invert_lut_input(Const lut, int bit)
|
||||
{
|
||||
Const result(State::S0, GetSize(lut));
|
||||
for (int i = 0; i < GetSize(lut); i++) {
|
||||
int j = i ^ (1 << bit);
|
||||
result[j] = lut[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Const invert_lut_output(Const lut)
|
||||
{
|
||||
Const result(State::S0, GetSize(lut));
|
||||
for (int i = 0; i < GetSize(lut); i++)
|
||||
result[i] = (lut[i] == State::S1) ? State::S0 : State::S1;
|
||||
return result;
|
||||
}
|
||||
|
||||
void fold_input_inverters()
|
||||
{
|
||||
for (auto cell : module->selected_cells()) {
|
||||
auto found_type = lut_types.find(cell->type);
|
||||
if (found_type == lut_types.end())
|
||||
continue;
|
||||
const auto &type = found_type->second;
|
||||
for (const auto &ipin : type.inputs) {
|
||||
if (!cell->hasPort(ipin.first))
|
||||
continue;
|
||||
auto sig = cell->getPort(ipin.first);
|
||||
if (GetSize(sig) == 0)
|
||||
continue;
|
||||
SigBit bit = sigmap(sig[0]);
|
||||
auto inv = inverted_bits.find(bit);
|
||||
if (inv == inverted_bits.end())
|
||||
continue; // not the output of an inverter
|
||||
// Rewire to inverter input
|
||||
cell->unsetPort(ipin.first);
|
||||
cell->setPort(ipin.first, inv->second);
|
||||
// Rewrite init
|
||||
cell->setParam(ipin.second.init_param,
|
||||
invert_lut_input(cell->getParam(ipin.second.init_param), ipin.second.input_bit));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void fold_output_inverters()
|
||||
{
|
||||
pool<SigBit> used_bits;
|
||||
// Find bits that are actually used
|
||||
for (auto cell : module->selected_cells()) {
|
||||
for (auto conn : cell->connections()) {
|
||||
if (cell->output(conn.first))
|
||||
continue;
|
||||
for (auto bit : sigmap(conn.second))
|
||||
used_bits.insert(bit);
|
||||
}
|
||||
}
|
||||
// Find LUTs driving inverters
|
||||
// (create a vector to avoid iterate-and-modify issues)
|
||||
std::vector<std::pair<Cell *, Cell*>> lut_inv;
|
||||
for (auto cell : module->selected_cells()) {
|
||||
auto found_type = lut_types.find(cell->type);
|
||||
if (found_type == lut_types.end())
|
||||
continue;
|
||||
if (!cell->hasPort(ID::O))
|
||||
continue;
|
||||
auto o_sig = cell->getPort(ID::O);
|
||||
if (GetSize(o_sig) == 0)
|
||||
continue;
|
||||
SigBit o = sigmap(o_sig[0]);
|
||||
auto found_inv = inverter_input.find(o);
|
||||
if (found_inv == inverter_input.end())
|
||||
continue; // doesn't drive an inverter
|
||||
lut_inv.emplace_back(cell, found_inv->second);
|
||||
}
|
||||
for (auto pair : lut_inv) {
|
||||
Cell *orig_lut = pair.first;
|
||||
Cell *inv = pair.second;
|
||||
// Find the inverter output
|
||||
SigBit inv_y = sigmap(inv->getPort(ID::Y)[0]);
|
||||
// Inverter output might not actually be used; if all users were folded into inputs already
|
||||
if (!used_bits.count(inv_y))
|
||||
continue;
|
||||
// Create a duplicate of the LUT with an inverted output
|
||||
// (if the uninverted version becomes unused it will be swept away)
|
||||
Cell *dup_lut = module->addCell(NEW_ID, orig_lut->type);
|
||||
inv->unsetPort(ID::Y);
|
||||
dup_lut->setPort(ID::O, inv_y);
|
||||
for (auto conn : orig_lut->connections()) {
|
||||
if (conn.first == ID::O)
|
||||
continue;
|
||||
dup_lut->setPort(conn.first, conn.second);
|
||||
}
|
||||
for (auto param : orig_lut->parameters) {
|
||||
if (param.first == lut_types.at(orig_lut->type).output_param)
|
||||
dup_lut->parameters[param.first] = invert_lut_output(param.second);
|
||||
else
|
||||
dup_lut->parameters[param.first] = param.second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void operator()()
|
||||
{
|
||||
find_inverted_bits();
|
||||
fold_input_inverters();
|
||||
fold_output_inverters();
|
||||
}
|
||||
};
|
||||
|
||||
struct GatemateFoldInvPass : public Pass {
|
||||
GatemateFoldInvPass() : Pass("gatemate_foldinv", "fold inverters into Gatemate LUT trees") { }
|
||||
void help() override
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" gatemate_foldinv [selection]\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
log("This pass searches for $__CC_NOT cells and folds them into CC_LUT2, CC_L2T4\n");
|
||||
log("and CC_L2T5 cells as created by LUT tree mapping.\n");
|
||||
log("\n");
|
||||
}
|
||||
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
log_header(design, "Executing GATEMATE_FOLDINV pass (folding LUT tree inverters).\n");
|
||||
|
||||
size_t argidx = 1;
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
for (Module *module : design->selected_modules()) {
|
||||
FoldInvWorker worker(module);
|
||||
worker();
|
||||
}
|
||||
}
|
||||
} GatemateFoldInvPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
// Any inverters not folded into LUTs are mapped to a LUT of their own
|
||||
module \$__CC_NOT (input A, output Y);
|
||||
CC_LUT1 #(.INIT(2'b01)) _TECHMAP_REPLACE_ (.I0(A), .O(Y));
|
||||
endmodule
|
|
@ -0,0 +1,323 @@
|
|||
#!/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 <cell-name> <cell-area> <cell-logic-function>
|
||||
#
|
||||
# PIN <pin-name> <phase> <input-load> <max-load>
|
||||
# <rise-block-delay> <rise-fanout-delay>
|
||||
# <fall-block-delay> <fall-fanout-delay>
|
||||
#
|
||||
# phase:
|
||||
# INV, NONINV, or UNKNOWN
|
||||
#
|
||||
# cell-logic-function:
|
||||
# <output> = <term with *(AND), +(OR), !(NOT)>
|
||||
|
||||
|
||||
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)
|
|
@ -67,7 +67,10 @@ struct SynthGateMatePass : public ScriptPass
|
|||
log("\n");
|
||||
log(" -nomx8, -nomx4\n");
|
||||
log(" do not use CC_MX{8,4} multiplexer cells in output netlist.\n");
|
||||
log("\n");;
|
||||
log("\n");
|
||||
log(" -luttree\n");
|
||||
log(" use new LUT tree mapping approach (EXPERIMENTAL).\n");
|
||||
log("\n");
|
||||
log(" -dff\n");
|
||||
log(" run 'abc' with -dff option\n");
|
||||
log("\n");
|
||||
|
@ -87,7 +90,7 @@ struct SynthGateMatePass : public ScriptPass
|
|||
}
|
||||
|
||||
string top_opt, vlog_file, json_file;
|
||||
bool noflatten, nobram, noaddf, nomult, nomx4, nomx8, dff, retime, noiopad, noclkbuf;
|
||||
bool noflatten, nobram, noaddf, nomult, nomx4, nomx8, luttree, dff, retime, noiopad, noclkbuf;
|
||||
|
||||
void clear_flags() override
|
||||
{
|
||||
|
@ -100,6 +103,7 @@ struct SynthGateMatePass : public ScriptPass
|
|||
nomult = false;
|
||||
nomx4 = false;
|
||||
nomx8 = false;
|
||||
luttree = false;
|
||||
dff = false;
|
||||
retime = false;
|
||||
noiopad = false;
|
||||
|
@ -158,6 +162,10 @@ struct SynthGateMatePass : public ScriptPass
|
|||
nomx8 = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-luttree") {
|
||||
luttree = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-dff") {
|
||||
dff = true;
|
||||
continue;
|
||||
|
@ -298,11 +306,23 @@ struct SynthGateMatePass : public ScriptPass
|
|||
|
||||
if (check_label("map_luts"))
|
||||
{
|
||||
if (luttree || help_mode) {
|
||||
std::string abc_args = " -genlib +/gatemate/lut_tree_cells.genlib";
|
||||
if (dff) {
|
||||
abc_args += " -dff";
|
||||
}
|
||||
run("abc " + abc_args, "(with -luttree)");
|
||||
run("techmap -map +/gatemate/lut_tree_map.v", "(with -luttree)");
|
||||
run("gatemate_foldinv", "(with -luttree)");
|
||||
run("techmap -map +/gatemate/inv_map.v", "(with -luttree)");
|
||||
}
|
||||
if (!luttree || help_mode) {
|
||||
std::string abc_args = " -dress -lut 4";
|
||||
if (dff) {
|
||||
abc_args += " -dff";
|
||||
}
|
||||
run("abc " + abc_args);
|
||||
run("abc " + abc_args, "(without -luttree)");
|
||||
}
|
||||
run("clean");
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue