mirror of
https://github.com/YosysHQ/yosys
synced 2025-04-08 10:25:19 +00:00
Merge pull request #717 from whitequark/opt_lut
Add a new opt_lut pass, which combines inefficiently packed LUTs
This commit is contained in:
commit
50a94ce4fc
3
Makefile
3
Makefile
|
@ -160,7 +160,7 @@ ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H"
|
|||
else ifeq ($(CONFIG),gcc-static)
|
||||
LD = $(CXX)
|
||||
LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -static
|
||||
LDLIBS := $(filter-out -lrt,$(LDLIBS))
|
||||
LDLIBS := $(filter-out -lrt,$(LDLIBS))
|
||||
CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS))
|
||||
CXXFLAGS += -std=c++11 -Os
|
||||
ABCMKARGS = CC="$(CC)" CXX="$(CXX)" LD="$(LD)" ABC_USE_LIBSTDCXX=1 LIBS="-lm -lpthread -static" OPTFLAGS="-O" \
|
||||
|
@ -578,6 +578,7 @@ test: $(TARGETS) $(EXTRA_TARGETS)
|
|||
+cd tests/various && bash run-test.sh
|
||||
+cd tests/sat && bash run-test.sh
|
||||
+cd tests/svinterfaces && bash run-test.sh $(SEEDOPT)
|
||||
+cd tests/opt && bash run-test.sh
|
||||
@echo ""
|
||||
@echo " Passed \"make test\"."
|
||||
@echo ""
|
||||
|
|
|
@ -6,6 +6,7 @@ OBJS += passes/opt/opt_reduce.o
|
|||
OBJS += passes/opt/opt_rmdff.o
|
||||
OBJS += passes/opt/opt_clean.o
|
||||
OBJS += passes/opt/opt_expr.o
|
||||
OBJS += passes/opt/opt_lut.o
|
||||
|
||||
ifneq ($(SMALL),1)
|
||||
OBJS += passes/opt/share.o
|
||||
|
|
478
passes/opt/opt_lut.cpp
Normal file
478
passes/opt/opt_lut.cpp
Normal file
|
@ -0,0 +1,478 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2018 whitequark <whitequark@whitequark.org>
|
||||
*
|
||||
* 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"
|
||||
#include "kernel/modtools.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
struct OptLutWorker
|
||||
{
|
||||
dict<IdString, dict<int, IdString>> &dlogic;
|
||||
RTLIL::Module *module;
|
||||
ModIndex index;
|
||||
SigMap sigmap;
|
||||
|
||||
pool<RTLIL::Cell*> luts;
|
||||
dict<RTLIL::Cell*, int> luts_arity;
|
||||
dict<RTLIL::Cell*, pool<RTLIL::Cell*>> luts_dlogics;
|
||||
dict<RTLIL::Cell*, pool<int>> luts_dlogic_inputs;
|
||||
|
||||
int combined_count = 0;
|
||||
|
||||
bool evaluate_lut(RTLIL::Cell *lut, dict<SigBit, bool> inputs)
|
||||
{
|
||||
SigSpec lut_input = sigmap(lut->getPort("\\A"));
|
||||
int lut_width = lut->getParam("\\WIDTH").as_int();
|
||||
Const lut_table = lut->getParam("\\LUT");
|
||||
int lut_index = 0;
|
||||
|
||||
for (int i = 0; i < lut_width; i++)
|
||||
{
|
||||
SigBit input = sigmap(lut_input[i]);
|
||||
if (inputs.count(input))
|
||||
{
|
||||
lut_index |= inputs[input] << i;
|
||||
}
|
||||
else
|
||||
{
|
||||
lut_index |= SigSpec(lut_input[i]).as_bool() << i;
|
||||
}
|
||||
}
|
||||
|
||||
return lut_table.extract(lut_index).as_int();
|
||||
}
|
||||
|
||||
void show_stats_by_arity()
|
||||
{
|
||||
dict<int, int> arity_counts;
|
||||
dict<IdString, int> dlogic_counts;
|
||||
int max_arity = 0;
|
||||
|
||||
for (auto lut_arity : luts_arity)
|
||||
{
|
||||
max_arity = max(max_arity, lut_arity.second);
|
||||
arity_counts[lut_arity.second]++;
|
||||
}
|
||||
|
||||
for (auto &lut_dlogics : luts_dlogics)
|
||||
{
|
||||
for (auto &lut_dlogic : lut_dlogics.second)
|
||||
{
|
||||
dlogic_counts[lut_dlogic->type]++;
|
||||
}
|
||||
}
|
||||
|
||||
log("Number of LUTs: %8zu\n", luts.size());
|
||||
for (int arity = 1; arity <= max_arity; arity++)
|
||||
{
|
||||
if (arity_counts[arity])
|
||||
log(" %d-LUT %16d\n", arity, arity_counts[arity]);
|
||||
}
|
||||
for (auto &dlogic_count : dlogic_counts)
|
||||
{
|
||||
log(" with %-12s %4d\n", dlogic_count.first.c_str(), dlogic_count.second);
|
||||
}
|
||||
}
|
||||
|
||||
OptLutWorker(dict<IdString, dict<int, IdString>> &dlogic, RTLIL::Module *module) :
|
||||
dlogic(dlogic), module(module), index(module), sigmap(module)
|
||||
{
|
||||
log("Discovering LUTs.\n");
|
||||
for (auto cell : module->selected_cells())
|
||||
{
|
||||
if (cell->type == "$lut")
|
||||
{
|
||||
int lut_width = cell->getParam("\\WIDTH").as_int();
|
||||
SigSpec lut_input = cell->getPort("\\A");
|
||||
int lut_arity = 0;
|
||||
|
||||
log("Found $lut\\WIDTH=%d cell %s.%s.\n", lut_width, log_id(module), log_id(cell));
|
||||
luts.insert(cell);
|
||||
|
||||
// First, find all dedicated logic we're connected to. This results in an overapproximation
|
||||
// of such connections.
|
||||
pool<RTLIL::Cell*> lut_all_dlogics;
|
||||
for (int i = 0; i < lut_width; i++)
|
||||
{
|
||||
SigBit bit = lut_input[i];
|
||||
for (auto &port : index.query_ports(bit))
|
||||
{
|
||||
if (dlogic.count(port.cell->type))
|
||||
{
|
||||
auto &dlogic_map = dlogic[port.cell->type];
|
||||
if (dlogic_map.count(i))
|
||||
{
|
||||
if (port.port == dlogic_map[i])
|
||||
{
|
||||
lut_all_dlogics.insert(port.cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Second, make sure that the connection to dedicated logic is legal. If it is not legal,
|
||||
// it means one of the two things:
|
||||
// * The connection is spurious. I.e. this is dedicated logic that will be packed
|
||||
// with some other LUT, and it just happens to be conected to this LUT as well.
|
||||
// * The connection is illegal.
|
||||
// In either of these cases, we don't need to concern ourselves with preserving the connection
|
||||
// between this LUT and this dedicated logic cell.
|
||||
pool<RTLIL::Cell*> lut_legal_dlogics;
|
||||
pool<int> lut_dlogic_inputs;
|
||||
for (auto lut_dlogic : lut_all_dlogics)
|
||||
{
|
||||
auto &dlogic_map = dlogic[lut_dlogic->type];
|
||||
bool legal = true;
|
||||
for (auto &dlogic_conn : dlogic_map)
|
||||
{
|
||||
if (lut_width <= dlogic_conn.first)
|
||||
{
|
||||
log(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic));
|
||||
log(" LUT input A[%d] not present.\n", dlogic_conn.first);
|
||||
legal = false;
|
||||
break;
|
||||
}
|
||||
if (sigmap(lut_input[dlogic_conn.first]) != sigmap(lut_dlogic->getPort(dlogic_conn.second)))
|
||||
{
|
||||
log(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic));
|
||||
log(" LUT input A[%d] (wire %s) not connected to %s port %s (wire %s).\n", dlogic_conn.first, log_signal(lut_input[dlogic_conn.first]), lut_dlogic->type.c_str(), dlogic_conn.second.c_str(), log_signal(lut_dlogic->getPort(dlogic_conn.second)));
|
||||
legal = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (legal)
|
||||
{
|
||||
log(" LUT has legal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic));
|
||||
lut_legal_dlogics.insert(lut_dlogic);
|
||||
for (auto &dlogic_conn : dlogic_map)
|
||||
lut_dlogic_inputs.insert(dlogic_conn.first);
|
||||
}
|
||||
}
|
||||
|
||||
// Third, determine LUT arity. An n-wide LUT that has k constant inputs and m inputs shared with dedicated
|
||||
// logic implements an (n-k-m)-ary function.
|
||||
for (int i = 0; i < lut_width; i++)
|
||||
{
|
||||
SigBit bit = lut_input[i];
|
||||
if (bit.wire || lut_dlogic_inputs.count(i))
|
||||
lut_arity++;
|
||||
}
|
||||
|
||||
log(" Cell implements a %d-LUT.\n", lut_arity);
|
||||
luts_arity[cell] = lut_arity;
|
||||
luts_dlogics[cell] = lut_legal_dlogics;
|
||||
luts_dlogic_inputs[cell] = lut_dlogic_inputs;
|
||||
}
|
||||
}
|
||||
show_stats_by_arity();
|
||||
|
||||
log("\n");
|
||||
log("Combining LUTs.\n");
|
||||
pool<RTLIL::Cell*> worklist = luts;
|
||||
while (worklist.size())
|
||||
{
|
||||
auto lutA = worklist.pop();
|
||||
SigSpec lutA_input = sigmap(lutA->getPort("\\A"));
|
||||
SigSpec lutA_output = sigmap(lutA->getPort("\\Y")[0]);
|
||||
int lutA_width = lutA->getParam("\\WIDTH").as_int();
|
||||
int lutA_arity = luts_arity[lutA];
|
||||
pool<int> &lutA_dlogic_inputs = luts_dlogic_inputs[lutA];
|
||||
|
||||
auto lutA_output_ports = index.query_ports(lutA->getPort("\\Y"));
|
||||
if (lutA_output_ports.size() != 2)
|
||||
continue;
|
||||
|
||||
for (auto &port : lutA_output_ports)
|
||||
{
|
||||
if (port.cell == lutA)
|
||||
continue;
|
||||
|
||||
if (luts.count(port.cell))
|
||||
{
|
||||
auto lutB = port.cell;
|
||||
SigSpec lutB_input = sigmap(lutB->getPort("\\A"));
|
||||
SigSpec lutB_output = sigmap(lutB->getPort("\\Y")[0]);
|
||||
int lutB_width = lutB->getParam("\\WIDTH").as_int();
|
||||
int lutB_arity = luts_arity[lutB];
|
||||
pool<int> &lutB_dlogic_inputs = luts_dlogic_inputs[lutB];
|
||||
|
||||
log("Found %s.%s (cell A) feeding %s.%s (cell B).\n", log_id(module), log_id(lutA), log_id(module), log_id(lutB));
|
||||
|
||||
pool<SigBit> lutA_inputs;
|
||||
pool<SigBit> lutB_inputs;
|
||||
for (auto &bit : lutA_input)
|
||||
{
|
||||
if (bit.wire)
|
||||
lutA_inputs.insert(sigmap(bit));
|
||||
}
|
||||
for (auto &bit : lutB_input)
|
||||
{
|
||||
if (bit.wire)
|
||||
lutB_inputs.insert(sigmap(bit));
|
||||
}
|
||||
|
||||
pool<SigBit> common_inputs;
|
||||
for (auto &bit : lutA_inputs)
|
||||
{
|
||||
if (lutB_inputs.count(bit))
|
||||
common_inputs.insert(bit);
|
||||
}
|
||||
|
||||
int lutM_arity = lutA_arity + lutB_arity - 1 - common_inputs.size();
|
||||
if (lutA_dlogic_inputs.size())
|
||||
log(" Cell A is a %d-LUT with %zu dedicated connections. ", lutA_arity, lutA_dlogic_inputs.size());
|
||||
else
|
||||
log(" Cell A is a %d-LUT. ", lutA_arity);
|
||||
if (lutB_dlogic_inputs.size())
|
||||
log("Cell B is a %d-LUT with %zu dedicated connections.\n", lutB_arity, lutB_dlogic_inputs.size());
|
||||
else
|
||||
log("Cell B is a %d-LUT.\n", lutB_arity);
|
||||
log(" Cells share %zu input(s) and can be merged into one %d-LUT.\n", common_inputs.size(), lutM_arity);
|
||||
|
||||
const int COMBINE_A = 1, COMBINE_B = 2, COMBINE_EITHER = COMBINE_A | COMBINE_B;
|
||||
int combine_mask = 0;
|
||||
if (lutM_arity > lutA_width)
|
||||
{
|
||||
log(" Not combining LUTs into cell A (combined LUT wider than cell A).\n");
|
||||
}
|
||||
else if (lutB_dlogic_inputs.size() > 0)
|
||||
{
|
||||
log(" Not combining LUTs into cell A (cell B is connected to dedicated logic).\n");
|
||||
}
|
||||
else if (lutB->get_bool_attribute("\\lut_keep"))
|
||||
{
|
||||
log(" Not combining LUTs into cell A (cell B has attribute \\lut_keep).\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
combine_mask |= COMBINE_A;
|
||||
}
|
||||
if (lutM_arity > lutB_width)
|
||||
{
|
||||
log(" Not combining LUTs into cell B (combined LUT wider than cell B).\n");
|
||||
}
|
||||
else if (lutA_dlogic_inputs.size() > 0)
|
||||
{
|
||||
log(" Not combining LUTs into cell B (cell A is connected to dedicated logic).\n");
|
||||
}
|
||||
else if (lutA->get_bool_attribute("\\lut_keep"))
|
||||
{
|
||||
log(" Not combining LUTs into cell B (cell A has attribute \\lut_keep).\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
combine_mask |= COMBINE_B;
|
||||
}
|
||||
|
||||
int combine = combine_mask;
|
||||
if (combine == COMBINE_EITHER)
|
||||
{
|
||||
log(" Can combine into either cell.\n");
|
||||
if (lutA_arity == 1)
|
||||
{
|
||||
log(" Cell A is a buffer or inverter, combining into cell B.\n");
|
||||
combine = COMBINE_B;
|
||||
}
|
||||
else if (lutB_arity == 1)
|
||||
{
|
||||
log(" Cell B is a buffer or inverter, combining into cell A.\n");
|
||||
combine = COMBINE_A;
|
||||
}
|
||||
else
|
||||
{
|
||||
log(" Arbitrarily combining into cell A.\n");
|
||||
combine = COMBINE_A;
|
||||
}
|
||||
}
|
||||
|
||||
RTLIL::Cell *lutM, *lutR;
|
||||
pool<SigBit> lutM_inputs, lutR_inputs;
|
||||
pool<int> lutM_dlogic_inputs;
|
||||
if (combine == COMBINE_A)
|
||||
{
|
||||
log(" Combining LUTs into cell A.\n");
|
||||
lutM = lutA;
|
||||
lutM_inputs = lutA_inputs;
|
||||
lutM_dlogic_inputs = lutA_dlogic_inputs;
|
||||
lutR = lutB;
|
||||
lutR_inputs = lutB_inputs;
|
||||
}
|
||||
else if (combine == COMBINE_B)
|
||||
{
|
||||
log(" Combining LUTs into cell B.\n");
|
||||
lutM = lutB;
|
||||
lutM_inputs = lutB_inputs;
|
||||
lutM_dlogic_inputs = lutB_dlogic_inputs;
|
||||
lutR = lutA;
|
||||
lutR_inputs = lutA_inputs;
|
||||
}
|
||||
else
|
||||
{
|
||||
log(" Cannot combine LUTs.\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
pool<SigBit> lutR_unique;
|
||||
for (auto &bit : lutR_inputs)
|
||||
{
|
||||
if (!common_inputs.count(bit) && bit != lutA_output)
|
||||
lutR_unique.insert(bit);
|
||||
}
|
||||
|
||||
int lutM_width = lutM->getParam("\\WIDTH").as_int();
|
||||
SigSpec lutM_input = sigmap(lutM->getPort("\\A"));
|
||||
std::vector<SigBit> lutM_new_inputs;
|
||||
for (int i = 0; i < lutM_width; i++)
|
||||
{
|
||||
bool input_unused = false;
|
||||
if (sigmap(lutM_input[i]) == lutA_output)
|
||||
input_unused = true;
|
||||
if (!lutM_input[i].wire && !lutM_dlogic_inputs.count(i))
|
||||
input_unused = true;
|
||||
|
||||
if (input_unused && lutR_unique.size())
|
||||
{
|
||||
SigBit new_input = lutR_unique.pop();
|
||||
log(" Connecting input %d as %s.\n", i, log_signal(new_input));
|
||||
lutM_new_inputs.push_back(new_input);
|
||||
}
|
||||
else if (sigmap(lutM_input[i]) == lutA_output)
|
||||
{
|
||||
log(" Disconnecting cascade input %d.\n", i);
|
||||
lutM_new_inputs.push_back(SigBit());
|
||||
}
|
||||
else
|
||||
{
|
||||
log(" Leaving input %d as %s.\n", i, log_signal(lutM_input[i]));
|
||||
lutM_new_inputs.push_back(lutM_input[i]);
|
||||
}
|
||||
}
|
||||
log_assert(lutR_unique.size() == 0);
|
||||
|
||||
RTLIL::Const lutM_new_table(State::Sx, 1 << lutM_width);
|
||||
for (int eval = 0; eval < 1 << lutM_width; eval++)
|
||||
{
|
||||
dict<SigBit, bool> eval_inputs;
|
||||
for (size_t i = 0; i < lutM_new_inputs.size(); i++)
|
||||
{
|
||||
eval_inputs[lutM_new_inputs[i]] = (eval >> i) & 1;
|
||||
}
|
||||
eval_inputs[lutA_output] = evaluate_lut(lutA, eval_inputs);
|
||||
lutM_new_table[eval] = (RTLIL::State) evaluate_lut(lutB, eval_inputs);
|
||||
}
|
||||
|
||||
log(" Old truth table: %s.\n", lutM->getParam("\\LUT").as_string().c_str());
|
||||
log(" New truth table: %s.\n", lutM_new_table.as_string().c_str());
|
||||
|
||||
lutM->setParam("\\LUT", lutM_new_table);
|
||||
lutM->setPort("\\A", lutM_new_inputs);
|
||||
lutM->setPort("\\Y", lutB_output);
|
||||
|
||||
luts_arity[lutM] = lutM_arity;
|
||||
luts.erase(lutR);
|
||||
luts_arity.erase(lutR);
|
||||
lutR->module->remove(lutR);
|
||||
|
||||
worklist.insert(lutM);
|
||||
worklist.erase(lutR);
|
||||
|
||||
combined_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
show_stats_by_arity();
|
||||
}
|
||||
};
|
||||
|
||||
static void split(std::vector<std::string> &tokens, const std::string &text, char sep)
|
||||
{
|
||||
size_t start = 0, end = 0;
|
||||
while ((end = text.find(sep, start)) != std::string::npos) {
|
||||
tokens.push_back(text.substr(start, end - start));
|
||||
start = end + 1;
|
||||
}
|
||||
tokens.push_back(text.substr(start));
|
||||
}
|
||||
|
||||
struct OptLutPass : public Pass {
|
||||
OptLutPass() : Pass("opt_lut", "optimize LUT cells") { }
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" opt_lut [options] [selection]\n");
|
||||
log("\n");
|
||||
log("This pass combines cascaded $lut cells with unused inputs.\n");
|
||||
log("\n");
|
||||
log(" -dlogic <type>:<cell-port>=<LUT-input>[:<cell-port>=<LUT-input>...]\n");
|
||||
log(" preserve connections to dedicated logic cell <type> that has ports\n");
|
||||
log(" <cell-port> connected to LUT inputs <LUT-input>. this includes\n");
|
||||
log(" the case where both LUT and dedicated logic input are connected to\n");
|
||||
log(" the same constant.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
log_header(design, "Executing OPT_LUT pass (optimize LUTs).\n");
|
||||
|
||||
dict<IdString, dict<int, IdString>> dlogic;
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
if (args[argidx] == "-dlogic" && argidx+1 < args.size()) {
|
||||
std::vector<std::string> tokens;
|
||||
split(tokens, args[++argidx], ':');
|
||||
if (tokens.size() < 2)
|
||||
log_cmd_error("The -dlogic option requires at least one connection.\n");
|
||||
IdString type = "\\" + tokens[0];
|
||||
for (auto it = tokens.begin() + 1; it != tokens.end(); ++it) {
|
||||
std::vector<std::string> conn_tokens;
|
||||
split(conn_tokens, *it, '=');
|
||||
if (conn_tokens.size() != 2)
|
||||
log_cmd_error("Invalid format of -dlogic signal mapping.\n");
|
||||
IdString logic_port = "\\" + conn_tokens[0];
|
||||
int lut_input = atoi(conn_tokens[1].c_str());
|
||||
dlogic[type][lut_input] = logic_port;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
int total_count = 0;
|
||||
for (auto module : design->selected_modules())
|
||||
{
|
||||
OptLutWorker worker(dlogic, module);
|
||||
total_count += worker.combined_count;
|
||||
}
|
||||
if (total_count)
|
||||
design->scratchpad_set_bool("opt.did_something", true);
|
||||
log("\n");
|
||||
log("Combined %d LUTs.\n", total_count);
|
||||
}
|
||||
} OptLutPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
|
@ -63,6 +63,9 @@ struct SynthIce40Pass : public ScriptPass
|
|||
log(" -retime\n");
|
||||
log(" run 'abc' with -dff option\n");
|
||||
log("\n");
|
||||
log(" -relut\n");
|
||||
log(" combine LUTs after synthesis\n");
|
||||
log("\n");
|
||||
log(" -nocarry\n");
|
||||
log(" do not use SB_CARRY cells in output netlist\n");
|
||||
log("\n");
|
||||
|
@ -90,7 +93,7 @@ struct SynthIce40Pass : public ScriptPass
|
|||
}
|
||||
|
||||
string top_opt, blif_file, edif_file, json_file;
|
||||
bool nocarry, nodffe, nobram, flatten, retime, abc2, vpr;
|
||||
bool nocarry, nodffe, nobram, flatten, retime, relut, abc2, vpr;
|
||||
int min_ce_use;
|
||||
|
||||
void clear_flags() YS_OVERRIDE
|
||||
|
@ -105,6 +108,7 @@ struct SynthIce40Pass : public ScriptPass
|
|||
nobram = false;
|
||||
flatten = true;
|
||||
retime = false;
|
||||
relut = false;
|
||||
abc2 = false;
|
||||
vpr = false;
|
||||
}
|
||||
|
@ -153,6 +157,10 @@ struct SynthIce40Pass : public ScriptPass
|
|||
retime = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-relut") {
|
||||
relut = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-nocarry") {
|
||||
nocarry = true;
|
||||
continue;
|
||||
|
@ -259,6 +267,10 @@ struct SynthIce40Pass : public ScriptPass
|
|||
run("techmap -map +/ice40/latches_map.v");
|
||||
run("abc -lut 4");
|
||||
run("clean");
|
||||
if (relut || help_mode) {
|
||||
run("ice40_unlut", " (only if -relut)");
|
||||
run("opt_lut -dlogic SB_CARRY:I0=1:I1=2:CI=3", "(only if -relut)");
|
||||
}
|
||||
}
|
||||
|
||||
if (check_label("map_cells"))
|
||||
|
|
1
tests/opt/.gitignore
vendored
Normal file
1
tests/opt/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
*.log
|
3
tests/opt/ice40_carry.v
Normal file
3
tests/opt/ice40_carry.v
Normal file
|
@ -0,0 +1,3 @@
|
|||
module SB_CARRY (output CO, input I0, I1, CI);
|
||||
assign CO = (I0 && I1) || ((I0 || I1) && CI);
|
||||
endmodule
|
18
tests/opt/opt_lut.v
Normal file
18
tests/opt/opt_lut.v
Normal file
|
@ -0,0 +1,18 @@
|
|||
module top(
|
||||
input [8:0] a,
|
||||
input [8:0] b,
|
||||
output [8:0] o1,
|
||||
output [2:0] o2,
|
||||
input [2:0] c,
|
||||
input [2:0] d,
|
||||
output [2:0] o3,
|
||||
output [2:0] o4,
|
||||
input s
|
||||
);
|
||||
|
||||
assign o1 = (s ? 0 : a + b);
|
||||
assign o2 = (s ? a : a - b);
|
||||
assign o3 = (s ? 4'b1111 : d + c);
|
||||
assign o4 = (s ? d : c - d);
|
||||
|
||||
endmodule
|
15
tests/opt/opt_lut.ys
Normal file
15
tests/opt/opt_lut.ys
Normal file
|
@ -0,0 +1,15 @@
|
|||
read_verilog opt_lut.v
|
||||
synth_ice40
|
||||
ice40_unlut
|
||||
design -save preopt
|
||||
|
||||
opt_lut -dlogic SB_CARRY:I0=1:I1=2:CI=3
|
||||
design -stash postopt
|
||||
|
||||
design -copy-from preopt -as preopt top
|
||||
design -copy-from postopt -as postopt top
|
||||
equiv_make preopt postopt equiv
|
||||
techmap -map ice40_carry.v
|
||||
prep -flatten -top equiv
|
||||
equiv_induct
|
||||
equiv_status -assert
|
6
tests/opt/run-test.sh
Executable file
6
tests/opt/run-test.sh
Executable file
|
@ -0,0 +1,6 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
for x in *.ys; do
|
||||
echo "Running $x.."
|
||||
../../yosys -ql ${x%.ys}.log $x
|
||||
done
|
Loading…
Reference in a new issue