Merge remote-tracking branch 'origin/master' into eddie/abc9_refactor

This commit is contained in:
Eddie Hung 2020-01-21 16:27:40 -08:00
commit 3d9737c1bd
16 changed files with 365 additions and 156 deletions

View File

@ -412,6 +412,8 @@ struct EdifBackend : public Backend {
for (auto &ref : it.second) for (auto &ref : it.second)
log_warning("Exporting x-bit on %s as zero bit.\n", ref.c_str()); log_warning("Exporting x-bit on %s as zero bit.\n", ref.c_str());
sig = RTLIL::State::S0; sig = RTLIL::State::S0;
} else if (sig == RTLIL::State::Sz) {
continue;
} else { } else {
for (auto &ref : it.second) for (auto &ref : it.second)
log_error("Don't know how to handle %s on %s.\n", log_signal(sig), ref.c_str()); log_error("Don't know how to handle %s on %s.\n", log_signal(sig), ref.c_str());

View File

@ -34,13 +34,20 @@ static SigSet<sig2driver_entry_t> sig2driver, sig2user;
static std::set<RTLIL::Cell*> muxtree_cells; static std::set<RTLIL::Cell*> muxtree_cells;
static SigPool sig_at_port; static SigPool sig_at_port;
static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, pool<Cell*> &recursion_monitor) static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, pool<Cell*> &recursion_monitor, dict<RTLIL::SigSpec, bool> &mux_tree_cache)
{ {
if (mux_tree_cache.find(sig) != mux_tree_cache.end())
return mux_tree_cache.at(sig);
if (sig.is_fully_const() || old_sig == sig) { if (sig.is_fully_const() || old_sig == sig) {
ret_true:
mux_tree_cache[sig] = true;
return true; return true;
} }
if (sig_at_port.check_any(assign_map(sig))) { if (sig_at_port.check_any(assign_map(sig))) {
ret_false:
mux_tree_cache[sig] = false;
return false; return false;
} }
@ -49,13 +56,13 @@ static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, poo
for (auto &cellport : cellport_list) for (auto &cellport : cellport_list)
{ {
if ((cellport.first->type != "$mux" && cellport.first->type != "$pmux") || cellport.second != "\\Y") { if ((cellport.first->type != "$mux" && cellport.first->type != "$pmux") || cellport.second != "\\Y") {
return false; goto ret_false;
} }
if (recursion_monitor.count(cellport.first)) { if (recursion_monitor.count(cellport.first)) {
log_warning("logic loop in mux tree at signal %s in module %s.\n", log_warning("logic loop in mux tree at signal %s in module %s.\n",
log_signal(sig), RTLIL::id2cstr(module->name)); log_signal(sig), RTLIL::id2cstr(module->name));
return false; goto ret_false;
} }
recursion_monitor.insert(cellport.first); recursion_monitor.insert(cellport.first);
@ -63,22 +70,22 @@ static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, poo
RTLIL::SigSpec sig_a = assign_map(cellport.first->getPort("\\A")); RTLIL::SigSpec sig_a = assign_map(cellport.first->getPort("\\A"));
RTLIL::SigSpec sig_b = assign_map(cellport.first->getPort("\\B")); RTLIL::SigSpec sig_b = assign_map(cellport.first->getPort("\\B"));
if (!check_state_mux_tree(old_sig, sig_a, recursion_monitor)) { if (!check_state_mux_tree(old_sig, sig_a, recursion_monitor, mux_tree_cache)) {
recursion_monitor.erase(cellport.first); recursion_monitor.erase(cellport.first);
return false; goto ret_false;
} }
for (int i = 0; i < sig_b.size(); i += sig_a.size()) for (int i = 0; i < sig_b.size(); i += sig_a.size())
if (!check_state_mux_tree(old_sig, sig_b.extract(i, sig_a.size()), recursion_monitor)) { if (!check_state_mux_tree(old_sig, sig_b.extract(i, sig_a.size()), recursion_monitor, mux_tree_cache)) {
recursion_monitor.erase(cellport.first); recursion_monitor.erase(cellport.first);
return false; goto ret_false;
} }
recursion_monitor.erase(cellport.first); recursion_monitor.erase(cellport.first);
muxtree_cells.insert(cellport.first); muxtree_cells.insert(cellport.first);
} }
return true; goto ret_true;
} }
static bool check_state_users(RTLIL::SigSpec sig) static bool check_state_users(RTLIL::SigSpec sig)
@ -143,11 +150,12 @@ static void detect_fsm(RTLIL::Wire *wire)
pool<Cell*> recursion_monitor; pool<Cell*> recursion_monitor;
RTLIL::SigSpec sig_q = assign_map(cellport.first->getPort("\\Q")); RTLIL::SigSpec sig_q = assign_map(cellport.first->getPort("\\Q"));
RTLIL::SigSpec sig_d = assign_map(cellport.first->getPort("\\D")); RTLIL::SigSpec sig_d = assign_map(cellport.first->getPort("\\D"));
dict<RTLIL::SigSpec, bool> mux_tree_cache;
if (sig_q != assign_map(wire)) if (sig_q != assign_map(wire))
continue; continue;
looks_like_state_reg = check_state_mux_tree(sig_q, sig_d, recursion_monitor); looks_like_state_reg = check_state_mux_tree(sig_q, sig_d, recursion_monitor, mux_tree_cache);
looks_like_good_state_reg = check_state_users(sig_q); looks_like_good_state_reg = check_state_users(sig_q);
if (!looks_like_state_reg) if (!looks_like_state_reg)

View File

@ -73,11 +73,11 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
// SB_MAC16 Input Interface // SB_MAC16 Input Interface
SigSpec A = st.sigA; SigSpec A = st.sigA;
A.extend_u0(16, st.mul->getParam(ID(A_SIGNED)).as_bool()); A.extend_u0(16, st.mul->parameters.at(ID(A_SIGNED), State::S0).as_bool());
log_assert(GetSize(A) == 16); log_assert(GetSize(A) == 16);
SigSpec B = st.sigB; SigSpec B = st.sigB;
B.extend_u0(16, st.mul->getParam(ID(B_SIGNED)).as_bool()); B.extend_u0(16, st.mul->parameters.at(ID(B_SIGNED), State::S0).as_bool());
log_assert(GetSize(B) == 16); log_assert(GetSize(B) == 16);
SigSpec CD = st.sigCD; SigSpec CD = st.sigCD;
@ -248,8 +248,8 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
cell->setParam(ID(BOTADDSUB_CARRYSELECT), Const(0, 2)); cell->setParam(ID(BOTADDSUB_CARRYSELECT), Const(0, 2));
cell->setParam(ID(MODE_8x8), State::S0); cell->setParam(ID(MODE_8x8), State::S0);
cell->setParam(ID(A_SIGNED), st.mul->getParam(ID(A_SIGNED)).as_bool()); cell->setParam(ID(A_SIGNED), st.mul->parameters.at(ID(A_SIGNED), State::S0).as_bool());
cell->setParam(ID(B_SIGNED), st.mul->getParam(ID(B_SIGNED)).as_bool()); cell->setParam(ID(B_SIGNED), st.mul->parameters.at(ID(B_SIGNED), State::S0).as_bool());
if (st.ffO) { if (st.ffO) {
if (st.o_lo) if (st.o_lo)

View File

@ -56,11 +56,16 @@ code sigA sigB sigH
break; break;
sigH.append(O[i]); sigH.append(O[i]);
} }
// This sigM could have no users if downstream sinks (e.g. $add) is
// narrower than $mul result, for example
if (i == 0)
reject;
log_assert(nusers(O.extract_end(i)) <= 1); log_assert(nusers(O.extract_end(i)) <= 1);
endcode endcode
code argQ ffA ffAholdmux ffArstmux ffAholdpol ffArstpol sigA clock clock_pol code argQ ffA ffAholdmux ffArstmux ffAholdpol ffArstpol sigA clock clock_pol
if (mul->type != \SB_MAC16 || !param(mul, \A_REG).as_bool()) { if (mul->type != \SB_MAC16 || !param(mul, \A_REG, State::S0).as_bool()) {
argQ = sigA; argQ = sigA;
subpattern(in_dffe); subpattern(in_dffe);
if (dff) { if (dff) {
@ -81,7 +86,7 @@ code argQ ffA ffAholdmux ffArstmux ffAholdpol ffArstpol sigA clock clock_pol
endcode endcode
code argQ ffB ffBholdmux ffBrstmux ffBholdpol ffBrstpol sigB clock clock_pol code argQ ffB ffBholdmux ffBrstmux ffBholdpol ffBrstpol sigB clock clock_pol
if (mul->type != \SB_MAC16 || !param(mul, \B_REG).as_bool()) { if (mul->type != \SB_MAC16 || !param(mul, \B_REG, State::S0).as_bool()) {
argQ = sigB; argQ = sigB;
subpattern(in_dffe); subpattern(in_dffe);
if (dff) { if (dff) {
@ -104,7 +109,7 @@ endcode
code argD ffFJKG sigH clock clock_pol code argD ffFJKG sigH clock clock_pol
if (nusers(sigH) == 2 && if (nusers(sigH) == 2 &&
(mul->type != \SB_MAC16 || (mul->type != \SB_MAC16 ||
(!param(mul, \TOP_8x8_MULT_REG).as_bool() && !param(mul, \BOT_8x8_MULT_REG).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG1).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG1).as_bool()))) { (!param(mul, \TOP_8x8_MULT_REG, State::S0).as_bool() && !param(mul, \BOT_8x8_MULT_REG, State::S0).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG1, State::S0).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG1, State::S0).as_bool()))) {
argD = sigH; argD = sigH;
subpattern(out_dffe); subpattern(out_dffe);
if (dff) { if (dff) {
@ -143,7 +148,7 @@ endcode
code argD ffH sigH sigO clock clock_pol code argD ffH sigH sigO clock clock_pol
if (ffFJKG && nusers(sigH) == 2 && if (ffFJKG && nusers(sigH) == 2 &&
(mul->type != \SB_MAC16 || !param(mul, \PIPELINE_16x16_MULT_REG2).as_bool())) { (mul->type != \SB_MAC16 || !param(mul, \PIPELINE_16x16_MULT_REG2, State::S0).as_bool())) {
argD = sigH; argD = sigH;
subpattern(out_dffe); subpattern(out_dffe);
if (dff) { if (dff) {
@ -174,7 +179,7 @@ reject_ffH: ;
endcode endcode
match add match add
if mul->type != \SB_MAC16 || (param(mul, \TOPOUTPUT_SELECT).as_int() == 3 && param(mul, \BOTOUTPUT_SELECT).as_int() == 3) if mul->type != \SB_MAC16 || (param(mul, \TOPOUTPUT_SELECT, State::S0).as_int() == 3 && param(mul, \BOTOUTPUT_SELECT, State::S0).as_int() == 3)
select add->type.in($add) select add->type.in($add)
choice <IdString> AB {\A, \B} choice <IdString> AB {\A, \B}
@ -200,7 +205,7 @@ code sigCD sigO cd_signed
if ((actual_acc_width > actual_mul_width) && (natural_mul_width > actual_mul_width)) if ((actual_acc_width > actual_mul_width) && (natural_mul_width > actual_mul_width))
reject; reject;
// If accumulator, check adder width and signedness // If accumulator, check adder width and signedness
if (sigCD == sigH && (actual_acc_width != actual_mul_width) && (param(mul, \A_SIGNED).as_bool() != param(add, \A_SIGNED).as_bool())) if (sigCD == sigH && (actual_acc_width != actual_mul_width) && (param(mul, \A_SIGNED, State::S0).as_bool() != param(add, \A_SIGNED).as_bool()))
reject; reject;
sigO = port(add, \Y); sigO = port(add, \Y);
@ -275,7 +280,7 @@ endcode
code argQ ffCD ffCDholdmux ffCDholdpol ffCDrstpol sigCD clock clock_pol code argQ ffCD ffCDholdmux ffCDholdpol ffCDrstpol sigCD clock clock_pol
if (!sigCD.empty() && sigCD != sigO && if (!sigCD.empty() && sigCD != sigO &&
(mul->type != \SB_MAC16 || (!param(mul, \C_REG).as_bool() && !param(mul, \D_REG).as_bool()))) { (mul->type != \SB_MAC16 || (!param(mul, \C_REG, State::S0).as_bool() && !param(mul, \D_REG, State::S0).as_bool()))) {
argQ = sigCD; argQ = sigCD;
subpattern(in_dffe); subpattern(in_dffe);
if (dff) { if (dff) {
@ -328,6 +333,8 @@ arg argD argQ clock clock_pol
code code
dff = nullptr; dff = nullptr;
if (argQ.empty())
reject;
for (auto c : argQ.chunks()) { for (auto c : argQ.chunks()) {
if (!c.wire) if (!c.wire)
reject; reject;

View File

@ -120,7 +120,7 @@ endcode
// reset functionality, using a subpattern discussed above) // reset functionality, using a subpattern discussed above)
// If matched, treat 'A' input as input of ADREG // If matched, treat 'A' input as input of ADREG
code argQ ffAD ffADcemux ffADrstmux ffADcepol ffADrstpol sigA clock code argQ ffAD ffADcemux ffADrstmux ffADcepol ffADrstpol sigA clock
if (param(dsp, \ADREG).as_int() == 0) { if (param(dsp, \ADREG, 1).as_int() == 0) {
argQ = sigA; argQ = sigA;
subpattern(in_dffe); subpattern(in_dffe);
if (dff) { if (dff) {
@ -176,7 +176,7 @@ code argQ ffAD ffADcemux ffADrstmux ffADcepol ffADrstpol sigA clock ffA2 ffA2cem
// Only search for ffA2 if there was a pre-adder // Only search for ffA2 if there was a pre-adder
// (otherwise ffA2 would have been matched as ffAD) // (otherwise ffA2 would have been matched as ffAD)
if (preAdd) { if (preAdd) {
if (param(dsp, \AREG).as_int() == 0) { if (param(dsp, \AREG, 1).as_int() == 0) {
argQ = sigA; argQ = sigA;
subpattern(in_dffe); subpattern(in_dffe);
if (dff) { if (dff) {
@ -237,7 +237,7 @@ endcode
// (5) Match 'B' input for B2REG // (5) Match 'B' input for B2REG
// If B2REG, then match 'B' input for B1REG // If B2REG, then match 'B' input for B1REG
code argQ ffB2 ffB2cemux ffB2rstmux ffB2cepol ffBrstpol sigB clock ffB1 ffB1cemux ffB1rstmux ffB1cepol code argQ ffB2 ffB2cemux ffB2rstmux ffB2cepol ffBrstpol sigB clock ffB1 ffB1cemux ffB1rstmux ffB1cepol
if (param(dsp, \BREG).as_int() == 0) { if (param(dsp, \BREG, 1).as_int() == 0) {
argQ = sigB; argQ = sigB;
subpattern(in_dffe); subpattern(in_dffe);
if (dff) { if (dff) {
@ -287,7 +287,7 @@ endcode
// (6) Match 'D' input for DREG // (6) Match 'D' input for DREG
code argQ ffD ffDcemux ffDrstmux ffDcepol ffDrstpol sigD clock code argQ ffD ffDcemux ffDrstmux ffDcepol ffDrstpol sigD clock
if (param(dsp, \DREG).as_int() == 0) { if (param(dsp, \DREG, 1).as_int() == 0) {
argQ = sigD; argQ = sigD;
subpattern(in_dffe); subpattern(in_dffe);
if (dff) { if (dff) {
@ -308,7 +308,7 @@ endcode
// (7) Match 'P' output that exclusively drives an MREG // (7) Match 'P' output that exclusively drives an MREG
code argD ffM ffMcemux ffMrstmux ffMcepol ffMrstpol sigM sigP clock code argD ffM ffMcemux ffMrstmux ffMcepol ffMrstpol sigM sigP clock
if (param(dsp, \MREG).as_int() == 0 && nusers(sigM) == 2) { if (param(dsp, \MREG, 1).as_int() == 0 && nusers(sigM) == 2) {
argD = sigM; argD = sigM;
subpattern(out_dffe); subpattern(out_dffe);
if (dff) { if (dff) {
@ -335,7 +335,7 @@ endcode
// recognised in xilinx_dsp.cc). // recognised in xilinx_dsp.cc).
match postAdd match postAdd
// Ensure that Z mux is not already used // Ensure that Z mux is not already used
if port(dsp, \OPMODE, SigSpec()).extract(4,3).is_fully_zero() if port(dsp, \OPMODE, SigSpec(0, 7)).extract(4,3).is_fully_zero()
select postAdd->type.in($add) select postAdd->type.in($add)
select GetSize(port(postAdd, \Y)) <= 48 select GetSize(port(postAdd, \Y)) <= 48
@ -363,7 +363,7 @@ endcode
// (9) Match 'P' output that exclusively drives a PREG // (9) Match 'P' output that exclusively drives a PREG
code argD ffP ffPcemux ffPrstmux ffPcepol ffPrstpol sigP clock code argD ffP ffPcemux ffPrstmux ffPcepol ffPrstpol sigP clock
if (param(dsp, \PREG).as_int() == 0) { if (param(dsp, \PREG, 1).as_int() == 0) {
int users = 2; int users = 2;
// If ffMcemux and no postAdd new-value net must have three users: ffMcemux, ffM and ffPcemux // If ffMcemux and no postAdd new-value net must have three users: ffMcemux, ffM and ffPcemux
if (ffMcemux && !postAdd) users++; if (ffMcemux && !postAdd) users++;
@ -460,7 +460,7 @@ arg argD argQ clock
code code
dff = nullptr; dff = nullptr;
if (GetSize(argQ) == 0) if (argQ.empty())
reject; reject;
for (const auto &c : argQ.chunks()) { for (const auto &c : argQ.chunks()) {
// Abandon matches when 'Q' is a constant // Abandon matches when 'Q' is a constant

View File

@ -12,4 +12,5 @@ OBJS += passes/sat/supercover.o
OBJS += passes/sat/fmcombine.o OBJS += passes/sat/fmcombine.o
OBJS += passes/sat/mutate.o OBJS += passes/sat/mutate.o
OBJS += passes/sat/cutpoint.o OBJS += passes/sat/cutpoint.o
OBJS += passes/sat/fminit.o

197
passes/sat/fminit.cc Normal file
View File

@ -0,0 +1,197 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
*
* 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 FminitPass : public Pass {
FminitPass() : Pass("fminit", "set init values/sequences for formal") { }
void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" fminit [options] <selection>\n");
log("\n");
log("This pass creates init constraints (for example for reset sequences) in a formal\n");
log("model.\n");
log("\n");
log(" -seq <signal> <sequence>\n");
log(" Set sequence using comma-separated list of values, use 'z for\n");
log(" unconstrained bits. The last value is used for the remainder of the\n");
log(" trace.\n");
log("\n");
log(" -set <signal> <value>\n");
log(" Add constant value constraint\n");
log("\n");
log(" -posedge <signal>\n");
log(" -negedge <signal>\n");
log(" Set clock for init sequences\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
vector<pair<string, vector<string>>> initdata;
vector<pair<string, string>> setdata;
string clocksignal;
bool clockedge;
log_header(design, "Executing FMINIT pass.\n");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
{
// if (args[argidx] == "-o" && argidx+1 < args.size()) {
// filename = args[++argidx];
// continue;
// }
if (args[argidx] == "-seq" && argidx+2 < args.size()) {
string lhs = args[++argidx];
string rhs = args[++argidx];
initdata.push_back(make_pair(lhs, split_tokens(rhs, ",")));
continue;
}
if (args[argidx] == "-set" && argidx+2 < args.size()) {
string lhs = args[++argidx];
string rhs = args[++argidx];
setdata.push_back(make_pair(lhs, rhs));
continue;
}
if (args[argidx] == "-posedge" && argidx+1 < args.size()) {
clocksignal = args[++argidx];
clockedge = true;
continue;
}
if (args[argidx] == "-negedge" && argidx+1 < args.size()) {
clocksignal = args[++argidx];
clockedge = true;
continue;
}
break;
}
extra_args(args, argidx, design);
Module *module = nullptr;
for (auto mod : design->selected_modules()) {
if (module != nullptr)
log_error("'fminit' requires exactly one module to be selected.\n");
module = mod;
}
if (module == nullptr)
log_error("'fminit' requires exactly one module to be selected.\n");
SigSpec clksig;
if (!clocksignal.empty()) {
if (!SigSpec::parse(clksig, module, clocksignal))
log_error("Error parsing expression '%s'.\n", clocksignal.c_str());
}
for (auto &it : setdata)
{
SigSpec lhs, rhs;
if (!SigSpec::parse(lhs, module, it.first))
log_error("Error parsing expression '%s'.\n", it.first.c_str());
if (!SigSpec::parse_rhs(lhs, rhs, module, it.second))
log_error("Error parsing expression '%s'.\n", it.second.c_str());
SigSpec final_lhs, final_rhs;
for (int i = 0; i < GetSize(rhs); i++)
if (rhs[i] != State::Sz) {
final_lhs.append(lhs[i]);
final_rhs.append(rhs[i]);
}
if (!final_lhs.empty()) {
SigSpec eq = module->Eq(NEW_ID, final_lhs, final_rhs);
module->addAssume(NEW_ID, eq, State::S1);
}
}
vector<SigSpec> ctrlsig;
vector<SigSpec> ctrlsig_latched;
for (auto &it : initdata)
{
SigSpec lhs, rhs;
if (!SigSpec::parse(lhs, module, it.first))
log_error("Error parsing expression '%s'.\n", it.first.c_str());
for (int i = 0; i < GetSize(it.second); i++)
{
if (i >= GetSize(ctrlsig))
{
SigSpec insig = i > 0 ? ctrlsig.at(i-1) : State::S0;
Wire *outwire = module->addWire(NEW_ID);
outwire->attributes[ID(init)] = i > 0 ? State::S0 : State::S1;
if (clksig.empty())
module->addFf(NEW_ID, insig, outwire);
else
module->addDff(NEW_ID, clksig, insig, outwire, clockedge);
ctrlsig.push_back(outwire);
ctrlsig_latched.push_back(SigSpec());
}
if (i+1 == GetSize(it.second) && ctrlsig_latched[i].empty())
{
Wire *ffwire = module->addWire(NEW_ID);
ffwire->attributes[ID(init)] = State::S0;
SigSpec outsig = module->Or(NEW_ID, ffwire, ctrlsig[i]);
if (clksig.empty())
module->addFf(NEW_ID, outsig, ffwire);
else
module->addDff(NEW_ID, clksig, outsig, ffwire, clockedge);
ctrlsig_latched[i] = outsig;
}
SigSpec ctrl = i+1 == GetSize(it.second) ? ctrlsig_latched[i] : ctrlsig[i];
SigSpec final_lhs, final_rhs;
if (!SigSpec::parse_rhs(lhs, rhs, module, it.second[i]))
log_error("Error parsing expression '%s'.\n", it.second[i].c_str());
for (int i = 0; i < GetSize(rhs); i++)
if (rhs[i] != State::Sz) {
final_lhs.append(lhs[i]);
final_rhs.append(rhs[i]);
}
if (!final_lhs.empty()) {
SigSpec eq = module->Eq(NEW_ID, final_lhs, final_rhs);
module->addAssume(NEW_ID, eq, ctrl);
}
}
}
}
} FminitPass;
PRIVATE_NAMESPACE_END

View File

@ -78,10 +78,12 @@ struct Ice40FfinitPass : public Pass {
continue; continue;
if (initbits.count(bit)) { if (initbits.count(bit)) {
if (initbits.at(bit) != val) if (initbits.at(bit) != val) {
log_error("Conflicting init values for signal %s (%s = %s, %s = %s).\n", log_warning("Conflicting init values for signal %s (%s = %s, %s = %s).\n",
log_signal(bit), log_signal(SigBit(wire, i)), log_signal(val), log_signal(bit), log_signal(SigBit(wire, i)), log_signal(val),
log_signal(initbit_to_wire[bit]), log_signal(initbits.at(bit))); log_signal(initbit_to_wire[bit]), log_signal(initbits.at(bit)));
initbits.at(bit) = State::Sx;
}
continue; continue;
} }
@ -114,6 +116,10 @@ struct Ice40FfinitPass : public Pass {
continue; continue;
State val = initbits.at(bit_q); State val = initbits.at(bit_q);
if (val == State::Sx)
continue;
handled_initbits.insert(bit_q); handled_initbits.insert(bit_q);
log("FF init value for cell %s (%s): %s = %c\n", log_id(cell), log_id(cell->type), log("FF init value for cell %s (%s): %s = %c\n", log_id(cell), log_id(cell->type),

View File

@ -273,7 +273,8 @@ struct SynthIce40Pass : public ScriptPass
run("opt_expr"); run("opt_expr");
run("opt_clean"); run("opt_clean");
if (help_mode || dsp) { if (help_mode || dsp) {
run("memory_dff"); run("memory_dff"); // ice40_dsp will merge registers, reserve memory port registers first
run("wreduce t:$mul");
run("techmap -map +/mul2dsp.v -map +/ice40/dsp_map.v -D DSP_A_MAXWIDTH=16 -D DSP_B_MAXWIDTH=16 " run("techmap -map +/mul2dsp.v -map +/ice40/dsp_map.v -D DSP_A_MAXWIDTH=16 -D DSP_B_MAXWIDTH=16 "
"-D DSP_A_MINWIDTH=2 -D DSP_B_MINWIDTH=2 -D DSP_Y_MINWIDTH=11 " "-D DSP_A_MINWIDTH=2 -D DSP_B_MINWIDTH=2 -D DSP_Y_MINWIDTH=11 "
"-D DSP_NAME=$__MUL16X16", "(if -dsp)"); "-D DSP_NAME=$__MUL16X16", "(if -dsp)");

View File

@ -33,7 +33,21 @@ module _80_xilinx_lcu (P, G, CI, CO);
genvar i; genvar i;
`ifdef _CLB_CARRY `ifdef _EXPLICIT_CARRY
wire [WIDTH-1:0] C = {CO, CI};
wire [WIDTH-1:0] S = P & ~G;
generate for (i = 0; i < WIDTH; i = i + 1) begin:slice
MUXCY muxcy (
.CI(C[i]),
.DI(G[i]),
.S(S[i]),
.O(CO[i])
);
end endgenerate
`else
localparam CARRY4_COUNT = (WIDTH + 3) / 4; localparam CARRY4_COUNT = (WIDTH + 3) / 4;
localparam MAX_WIDTH = CARRY4_COUNT * 4; localparam MAX_WIDTH = CARRY4_COUNT * 4;
@ -53,9 +67,9 @@ module _80_xilinx_lcu (P, G, CI, CO);
( (
.CYINIT(CI), .CYINIT(CI),
.CI (1'd0), .CI (1'd0),
.DI (G [(Y_WIDTH - 1):i*4]), .DI (G [(WIDTH - 1):i*4]),
.S (S [(Y_WIDTH - 1):i*4]), .S (S [(WIDTH - 1):i*4]),
.CO (CO[(Y_WIDTH - 1):i*4]), .CO (CO[(WIDTH - 1):i*4]),
); );
// Another one // Another one
end else begin end else begin
@ -63,9 +77,9 @@ module _80_xilinx_lcu (P, G, CI, CO);
( (
.CYINIT(1'd0), .CYINIT(1'd0),
.CI (C [i*4 - 1]), .CI (C [i*4 - 1]),
.DI (G [(Y_WIDTH - 1):i*4]), .DI (G [(WIDTH - 1):i*4]),
.S (S [(Y_WIDTH - 1):i*4]), .S (S [(WIDTH - 1):i*4]),
.CO (CO[(Y_WIDTH - 1):i*4]), .CO (CO[(WIDTH - 1):i*4]),
); );
end end
@ -97,34 +111,6 @@ module _80_xilinx_lcu (P, G, CI, CO);
end end
end endgenerate end endgenerate
`elsif _EXPLICIT_CARRY
wire [WIDTH-1:0] C = {CO, CI};
wire [WIDTH-1:0] S = P & ~G;
generate for (i = 0; i < WIDTH; i = i + 1) begin:slice
MUXCY muxcy (
.CI(C[i]),
.DI(G[i]),
.S(S[i]),
.O(CO[i])
);
end endgenerate
`else
wire [WIDTH-1:0] C = {CO, CI};
wire [WIDTH-1:0] S = P & ~G;
generate for (i = 0; i < WIDTH; i = i + 1) begin:slice
MUXCY muxcy (
.CI(C[i]),
.DI(G[i]),
.S(S[i]),
.O(CO[i])
);
end endgenerate
`endif `endif
endmodule endmodule
@ -161,79 +147,7 @@ module _80_xilinx_alu (A, B, CI, BI, X, Y, CO);
genvar i; genvar i;
`ifdef _CLB_CARRY `ifdef _EXPLICIT_CARRY
localparam CARRY4_COUNT = (Y_WIDTH + 3) / 4;
localparam MAX_WIDTH = CARRY4_COUNT * 4;
localparam PAD_WIDTH = MAX_WIDTH - Y_WIDTH;
wire [MAX_WIDTH-1:0] S = {{PAD_WIDTH{1'b0}}, AA ^ BB};
wire [MAX_WIDTH-1:0] DI = {{PAD_WIDTH{1'b0}}, AA & BB};
wire [MAX_WIDTH-1:0] C = CO;
genvar i;
generate for (i = 0; i < CARRY4_COUNT; i = i + 1) begin:slice
// Partially occupied CARRY4
if ((i+1)*4 > Y_WIDTH) begin
// First one
if (i == 0) begin
CARRY4 carry4_1st_part
(
.CYINIT(CI),
.CI (1'd0),
.DI (DI[(Y_WIDTH - 1):i*4]),
.S (S [(Y_WIDTH - 1):i*4]),
.O (Y [(Y_WIDTH - 1):i*4]),
.CO (CO[(Y_WIDTH - 1):i*4])
);
// Another one
end else begin
CARRY4 carry4_part
(
.CYINIT(1'd0),
.CI (C [i*4 - 1]),
.DI (DI[(Y_WIDTH - 1):i*4]),
.S (S [(Y_WIDTH - 1):i*4]),
.O (Y [(Y_WIDTH - 1):i*4]),
.CO (CO[(Y_WIDTH - 1):i*4])
);
end
// Fully occupied CARRY4
end else begin
// First one
if (i == 0) begin
CARRY4 carry4_1st_full
(
.CYINIT(CI),
.CI (1'd0),
.DI (DI[((i+1)*4 - 1):i*4]),
.S (S [((i+1)*4 - 1):i*4]),
.O (Y [((i+1)*4 - 1):i*4]),
.CO (CO[((i+1)*4 - 1):i*4])
);
// Another one
end else begin
CARRY4 carry4_full
(
.CYINIT(1'd0),
.CI (C [i*4 - 1]),
.DI (DI[((i+1)*4 - 1):i*4]),
.S (S [((i+1)*4 - 1):i*4]),
.O (Y [((i+1)*4 - 1):i*4]),
.CO (CO[((i+1)*4 - 1):i*4])
);
end
end
end endgenerate
`elsif _EXPLICIT_CARRY
wire [Y_WIDTH-1:0] S = AA ^ BB; wire [Y_WIDTH-1:0] S = AA ^ BB;
wire [Y_WIDTH-1:0] DI = AA & BB; wire [Y_WIDTH-1:0] DI = AA & BB;
@ -333,23 +247,74 @@ module _80_xilinx_alu (A, B, CI, BI, X, Y, CO);
`else `else
wire [Y_WIDTH-1:0] S = AA ^ BB; localparam CARRY4_COUNT = (Y_WIDTH + 3) / 4;
wire [Y_WIDTH-1:0] DI = AA & BB; localparam MAX_WIDTH = CARRY4_COUNT * 4;
localparam PAD_WIDTH = MAX_WIDTH - Y_WIDTH;
wire [Y_WIDTH-1:0] C = {CO, CI}; wire [MAX_WIDTH-1:0] S = {{PAD_WIDTH{1'b0}}, AA ^ BB};
wire [MAX_WIDTH-1:0] DI = {{PAD_WIDTH{1'b0}}, AA & BB};
wire [MAX_WIDTH-1:0] C = CO;
genvar i;
generate for (i = 0; i < CARRY4_COUNT; i = i + 1) begin:slice
// Partially occupied CARRY4
if ((i+1)*4 > Y_WIDTH) begin
// First one
if (i == 0) begin
CARRY4 carry4_1st_part
(
.CYINIT(CI),
.CI (1'd0),
.DI (DI[(Y_WIDTH - 1):i*4]),
.S (S [(Y_WIDTH - 1):i*4]),
.O (Y [(Y_WIDTH - 1):i*4]),
.CO (CO[(Y_WIDTH - 1):i*4])
);
// Another one
end else begin
CARRY4 carry4_part
(
.CYINIT(1'd0),
.CI (C [i*4 - 1]),
.DI (DI[(Y_WIDTH - 1):i*4]),
.S (S [(Y_WIDTH - 1):i*4]),
.O (Y [(Y_WIDTH - 1):i*4]),
.CO (CO[(Y_WIDTH - 1):i*4])
);
end
// Fully occupied CARRY4
end else begin
// First one
if (i == 0) begin
CARRY4 carry4_1st_full
(
.CYINIT(CI),
.CI (1'd0),
.DI (DI[((i+1)*4 - 1):i*4]),
.S (S [((i+1)*4 - 1):i*4]),
.O (Y [((i+1)*4 - 1):i*4]),
.CO (CO[((i+1)*4 - 1):i*4])
);
// Another one
end else begin
CARRY4 carry4_full
(
.CYINIT(1'd0),
.CI (C [i*4 - 1]),
.DI (DI[((i+1)*4 - 1):i*4]),
.S (S [((i+1)*4 - 1):i*4]),
.O (Y [((i+1)*4 - 1):i*4]),
.CO (CO[((i+1)*4 - 1):i*4])
);
end
end
generate for (i = 0; i < Y_WIDTH; i = i + 1) begin:slice
MUXCY muxcy (
.CI(C[i]),
.DI(DI[i]),
.S(S[i]),
.O(CO[i])
);
XORCY xorcy (
.CI(C[i]),
.LI(S[i]),
.O(Y[i])
);
end endgenerate end endgenerate
`endif `endif

View File

@ -518,8 +518,6 @@ struct SynthXilinxPass : public ScriptPass
techmap_args += " -map +/xilinx/arith_map.v"; techmap_args += " -map +/xilinx/arith_map.v";
if (vpr) if (vpr)
techmap_args += " -D _EXPLICIT_CARRY"; techmap_args += " -D _EXPLICIT_CARRY";
else
techmap_args += " -D _CLB_CARRY";
} }
run("techmap " + techmap_args); run("techmap " + techmap_args);
run("opt -fast"); run("opt -fast");

Binary file not shown.

View File

@ -0,0 +1,2 @@
read_ilang bug1644.il.gz
synth_ice40 -top top -dsp -json adc_dac_pass_through.json -run :map_bram

View File

@ -0,0 +1,11 @@
read_verilog <<EOT
module top(input [15:0] a, b, output [31:0] o1, o2, o5);
SB_MAC16 m1 (.A(a), .B(16'd1234), .O(o1));
assign o2 = a * 16'd0;
wire [31:0] o3, o4;
SB_MAC16 m2 (.A(a), .B(b), .O(o3));
assign o4 = a * b;
SB_MAC16 m3 (.A(a), .B(b), .O(o5));
endmodule
EOT
ice40_dsp

View File

@ -0,0 +1,11 @@
read_verilog <<EOT
module top(input [24:0] a, input [17:0] b, output [42:0] o1, o2, o5);
DSP48E1 m1 (.A(a), .B(16'd1234), .P(o1));
assign o2 = a * 16'd0;
wire [42:0] o3, o4;
DSP48E1 m2 (.A(a), .B(b), .P(o3));
assign o4 = a * b;
DSP48E1 m3 (.A(a), .B(b), .P(o5));
endmodule
EOT
xilinx_dsp