Merge remote-tracking branch 'origin/master' into xaig_dff

This commit is contained in:
Eddie Hung 2019-12-30 14:31:42 -08:00
commit 405e974fe5
42 changed files with 1727 additions and 207 deletions

View File

@ -81,8 +81,7 @@ struct XAigerWriter
pool<SigBit> input_bits, output_bits, external_bits; pool<SigBit> input_bits, output_bits, external_bits;
dict<SigBit, SigBit> not_map, alias_map; dict<SigBit, SigBit> not_map, alias_map;
dict<SigBit, pair<SigBit, SigBit>> and_map; dict<SigBit, pair<SigBit, SigBit>> and_map;
vector<std::tuple<SigBit,RTLIL::Cell*,RTLIL::IdString,int>> ci_bits; vector<SigBit> ci_bits, co_bits;
vector<std::tuple<SigBit,RTLIL::Cell*,RTLIL::IdString,int,int>> co_bits;
dict<SigBit, std::pair<int,int>> ff_bits; dict<SigBit, std::pair<int,int>> ff_bits;
dict<SigBit, float> arrival_times; dict<SigBit, float> arrival_times;
@ -429,7 +428,6 @@ struct XAigerWriter
cell->setPort(port_name, rhs); cell->setPort(port_name, rhs);
} }
int offset = 0;
for (auto b : rhs.bits()) { for (auto b : rhs.bits()) {
SigBit I = sigmap(b); SigBit I = sigmap(b);
if (b == RTLIL::Sx) if (b == RTLIL::Sx)
@ -440,7 +438,7 @@ struct XAigerWriter
else else
alias_map[b] = I; alias_map[b] = I;
} }
co_bits.emplace_back(b, cell, port_name, offset++, 0); co_bits.emplace_back(b);
unused_bits.erase(I); unused_bits.erase(I);
} }
} }
@ -460,9 +458,8 @@ struct XAigerWriter
cell->setPort(port_name, rhs); cell->setPort(port_name, rhs);
} }
int offset = 0;
for (const auto &b : rhs.bits()) { for (const auto &b : rhs.bits()) {
ci_bits.emplace_back(b, cell, port_name, offset++); ci_bits.emplace_back(b);
SigBit O = sigmap(b); SigBit O = sigmap(b);
if (O != b) if (O != b)
alias_map[O] = b; alias_map[O] = b;
@ -478,7 +475,6 @@ struct XAigerWriter
if (rhs.empty()) if (rhs.empty())
log_error("'%s.$abc9_currQ' is not a wire present in module '%s'.\n", log_id(cell), log_id(module)); log_error("'%s.$abc9_currQ' is not a wire present in module '%s'.\n", log_id(cell), log_id(module));
int offset = 0;
for (auto b : rhs) { for (auto b : rhs) {
SigBit I = sigmap(b); SigBit I = sigmap(b);
if (b == RTLIL::Sx) if (b == RTLIL::Sx)
@ -489,7 +485,7 @@ struct XAigerWriter
else else
alias_map[b] = I; alias_map[b] = I;
} }
co_bits.emplace_back(b, cell, "\\$abc9_currQ", offset++, 0); co_bits.emplace_back(b);
unused_bits.erase(I); unused_bits.erase(I);
} }
} }
@ -571,17 +567,15 @@ struct XAigerWriter
} }
dict<SigBit, int> ff_aig_map; dict<SigBit, int> ff_aig_map;
for (auto &c : ci_bits) { for (auto &bit : ci_bits) {
RTLIL::SigBit bit = std::get<0>(c);
aig_m++, aig_i++; aig_m++, aig_i++;
auto r = aig_map.insert(std::make_pair(bit, 2*aig_m)); auto r = aig_map.insert(std::make_pair(bit, 2*aig_m));
if (!r.second) if (!r.second)
ff_aig_map[bit] = 2*aig_m; ff_aig_map[bit] = 2*aig_m;
} }
for (auto &c : co_bits) { for (auto bit : co_bits) {
RTLIL::SigBit bit = std::get<0>(c); ordered_outputs[bit] = aig_o++;
std::get<4>(c) = ordered_outputs[bit] = aig_o++;
aig_outputs.push_back(bit2aig(bit)); aig_outputs.push_back(bit2aig(bit));
} }
@ -707,27 +701,38 @@ struct XAigerWriter
int port_id = 1; int port_id = 1;
int box_count = 0; int box_count = 0;
for (auto cell : box_list) { for (auto cell : box_list) {
RTLIL::Module* box_module = module->design->module(cell->type); RTLIL::Module* orig_box_module = module->design->module(cell->type);
log_assert(box_module); log_assert(orig_box_module);
IdString derived_name = box_module->derive(module->design, cell->parameters); IdString derived_name = orig_box_module->derive(module->design, cell->parameters);
box_module = module->design->module(derived_name); RTLIL::Module* box_module = module->design->module(derived_name);
if (box_module->has_processes()) if (box_module->has_processes())
Pass::call_on_module(module->design, box_module, "proc"); log_error("ABC9 box '%s' contains processes!\n", box_module->name.c_str());
int box_inputs = 0, box_outputs = 0; int box_inputs = 0, box_outputs = 0;
auto r = cell_cache.insert(std::make_pair(derived_name, nullptr)); auto r = cell_cache.insert(std::make_pair(derived_name, nullptr));
Cell *holes_cell = r.first->second; Cell *holes_cell = r.first->second;
if (r.second && !holes_cell && box_module->get_bool_attribute("\\whitebox")) { if (r.second && box_module->get_bool_attribute("\\whitebox")) {
holes_cell = holes_module->addCell(cell->name, cell->type); holes_cell = holes_module->addCell(cell->name, cell->type);
holes_cell->parameters = cell->parameters; holes_cell->parameters = cell->parameters;
r.first->second = holes_cell; r.first->second = holes_cell;
// Since Module::derive() will create a new module, there
// is a chance that the ports will be alphabetically ordered
// again, which is a problem when carry-chains are involved.
// Inherit the port ordering from the original module here...
// (and set the port_id below, when iterating through those)
log_assert(GetSize(box_module->ports) == GetSize(orig_box_module->ports));
box_module->ports = orig_box_module->ports;
} }
// NB: Assume box_module->ports are sorted alphabetically // NB: Assume box_module->ports are sorted alphabetically
// (as RTLIL::Module::fixup_ports() would do) // (as RTLIL::Module::fixup_ports() would do)
int box_port_id = 1;
for (const auto &port_name : box_module->ports) { for (const auto &port_name : box_module->ports) {
RTLIL::Wire *w = box_module->wire(port_name); RTLIL::Wire *w = box_module->wire(port_name);
log_assert(w); log_assert(w);
if (r.second)
w->port_id = box_port_id++;
RTLIL::Wire *holes_wire; RTLIL::Wire *holes_wire;
RTLIL::SigSpec port_sig; RTLIL::SigSpec port_sig;
if (w->port_input) if (w->port_input)
@ -829,8 +834,9 @@ struct XAigerWriter
//holes_module->fixup_ports(); //holes_module->fixup_ports();
holes_module->check(); holes_module->check();
// TODO: Should techmap/aigmap/check all lib_whitebox-es just once, // Cannot techmap/aigmap/check all lib_whitebox-es outside of write_xaiger
// instead of per write_xaiger call // since boxes may contain parameters in which case `flatten` would have
// created a new $paramod ...
Pass::call_on_module(holes_module->design, holes_module, "flatten -wb; techmap; aigmap"); Pass::call_on_module(holes_module->design, holes_module, "flatten -wb; techmap; aigmap");
dict<SigSig, SigSig> replace; dict<SigSig, SigSig> replace;

View File

@ -22,8 +22,9 @@ $(eval $(call add_extra_objs,passes/pmgen/ice40_wrapcarry_pm.h))
# -------------------------------------- # --------------------------------------
OBJS += passes/pmgen/xilinx_dsp.o OBJS += passes/pmgen/xilinx_dsp.o
passes/pmgen/xilinx_dsp.o: passes/pmgen/xilinx_dsp_pm.h passes/pmgen/xilinx_dsp_CREG_pm.h passes/pmgen/xilinx_dsp_cascade_pm.h passes/pmgen/xilinx_dsp.o: passes/pmgen/xilinx_dsp_pm.h passes/pmgen/xilinx_dsp48a_pm.h passes/pmgen/xilinx_dsp_CREG_pm.h passes/pmgen/xilinx_dsp_cascade_pm.h
$(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_pm.h)) $(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_pm.h))
$(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp48a_pm.h))
$(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_CREG_pm.h)) $(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_CREG_pm.h))
$(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_cascade_pm.h)) $(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_cascade_pm.h))

View File

@ -26,6 +26,7 @@ USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN PRIVATE_NAMESPACE_BEGIN
#include "passes/pmgen/xilinx_dsp_pm.h" #include "passes/pmgen/xilinx_dsp_pm.h"
#include "passes/pmgen/xilinx_dsp48a_pm.h"
#include "passes/pmgen/xilinx_dsp_CREG_pm.h" #include "passes/pmgen/xilinx_dsp_CREG_pm.h"
#include "passes/pmgen/xilinx_dsp_cascade_pm.h" #include "passes/pmgen/xilinx_dsp_cascade_pm.h"
@ -487,6 +488,190 @@ void xilinx_dsp_pack(xilinx_dsp_pm &pm)
pm.blacklist(cell); pm.blacklist(cell);
} }
void xilinx_dsp48a_pack(xilinx_dsp48a_pm &pm)
{
auto &st = pm.st_xilinx_dsp48a_pack;
log("Analysing %s.%s for Xilinx DSP48A/DSP48A1 packing.\n", log_id(pm.module), log_id(st.dsp));
log_debug("preAdd: %s\n", log_id(st.preAdd, "--"));
log_debug("ffA1: %s %s %s\n", log_id(st.ffA1, "--"), log_id(st.ffA1cemux, "--"), log_id(st.ffA1rstmux, "--"));
log_debug("ffA0: %s %s %s\n", log_id(st.ffA0, "--"), log_id(st.ffA0cemux, "--"), log_id(st.ffA0rstmux, "--"));
log_debug("ffB1: %s %s %s\n", log_id(st.ffB1, "--"), log_id(st.ffB1cemux, "--"), log_id(st.ffB1rstmux, "--"));
log_debug("ffB0: %s %s %s\n", log_id(st.ffB0, "--"), log_id(st.ffB0cemux, "--"), log_id(st.ffB0rstmux, "--"));
log_debug("ffD: %s %s %s\n", log_id(st.ffD, "--"), log_id(st.ffDcemux, "--"), log_id(st.ffDrstmux, "--"));
log_debug("dsp: %s\n", log_id(st.dsp, "--"));
log_debug("ffM: %s %s %s\n", log_id(st.ffM, "--"), log_id(st.ffMcemux, "--"), log_id(st.ffMrstmux, "--"));
log_debug("postAdd: %s\n", log_id(st.postAdd, "--"));
log_debug("postAddMux: %s\n", log_id(st.postAddMux, "--"));
log_debug("ffP: %s %s %s\n", log_id(st.ffP, "--"), log_id(st.ffPcemux, "--"), log_id(st.ffPrstmux, "--"));
Cell *cell = st.dsp;
SigSpec &opmode = cell->connections_.at(ID(OPMODE));
if (st.preAdd) {
log(" preadder %s (%s)\n", log_id(st.preAdd), log_id(st.preAdd->type));
bool D_SIGNED = st.preAdd->getParam(ID(A_SIGNED)).as_bool();
bool B_SIGNED = st.preAdd->getParam(ID(B_SIGNED)).as_bool();
st.sigB.extend_u0(18, B_SIGNED);
st.sigD.extend_u0(18, D_SIGNED);
cell->setPort(ID(B), st.sigB);
cell->setPort(ID(D), st.sigD);
opmode[4] = State::S1;
if (st.preAdd->type == ID($add))
opmode[6] = State::S0;
else if (st.preAdd->type == ID($sub))
opmode[6] = State::S1;
else
log_assert(!"strange pre-adder type");
pm.autoremove(st.preAdd);
}
if (st.postAdd) {
log(" postadder %s (%s)\n", log_id(st.postAdd), log_id(st.postAdd->type));
if (st.postAddMux) {
log_assert(st.ffP);
opmode[2] = st.postAddMux->getPort(ID(S));
pm.autoremove(st.postAddMux);
}
else if (st.ffP && st.sigC == st.sigP)
opmode[2] = State::S0;
else
opmode[2] = State::S1;
opmode[3] = State::S1;
if (opmode[2] != State::S0) {
if (st.postAddMuxAB == ID(A))
st.sigC.extend_u0(48, st.postAdd->getParam(ID(B_SIGNED)).as_bool());
else
st.sigC.extend_u0(48, st.postAdd->getParam(ID(A_SIGNED)).as_bool());
cell->setPort(ID(C), st.sigC);
}
pm.autoremove(st.postAdd);
}
if (st.clock != SigBit())
{
cell->setPort(ID(CLK), st.clock);
auto f = [&pm,cell](SigSpec &A, Cell* ff, Cell* cemux, bool cepol, IdString ceport, Cell* rstmux, bool rstpol, IdString rstport) {
SigSpec D = ff->getPort(ID(D));
SigSpec Q = pm.sigmap(ff->getPort(ID(Q)));
if (!A.empty())
A.replace(Q, D);
if (rstmux) {
SigSpec Y = rstmux->getPort(ID(Y));
SigSpec AB = rstmux->getPort(rstpol ? ID(A) : ID(B));
if (!A.empty())
A.replace(Y, AB);
if (rstport != IdString()) {
SigSpec S = rstmux->getPort(ID(S));
cell->setPort(rstport, rstpol ? S : pm.module->Not(NEW_ID, S));
}
}
else if (rstport != IdString())
cell->setPort(rstport, State::S0);
if (cemux) {
SigSpec Y = cemux->getPort(ID(Y));
SigSpec BA = cemux->getPort(cepol ? ID(B) : ID(A));
SigSpec S = cemux->getPort(ID(S));
if (!A.empty())
A.replace(Y, BA);
cell->setPort(ceport, cepol ? S : pm.module->Not(NEW_ID, S));
}
else
cell->setPort(ceport, State::S1);
for (auto c : Q.chunks()) {
auto it = c.wire->attributes.find(ID(init));
if (it == c.wire->attributes.end())
continue;
for (int i = c.offset; i < c.offset+c.width; i++) {
log_assert(it->second[i] == State::S0 || it->second[i] == State::Sx);
it->second[i] = State::Sx;
}
}
};
if (st.ffA0 || st.ffA1) {
SigSpec A = cell->getPort(ID(A));
if (st.ffA1) {
f(A, st.ffA1, st.ffA1cemux, st.ffAcepol, ID(CEA), st.ffA1rstmux, st.ffArstpol, ID(RSTA));
cell->setParam(ID(A1REG), 1);
}
if (st.ffA0) {
f(A, st.ffA0, st.ffA0cemux, st.ffAcepol, ID(CEA), st.ffA0rstmux, st.ffArstpol, ID(RSTA));
cell->setParam(ID(A0REG), 1);
}
pm.add_siguser(A, cell);
cell->setPort(ID(A), A);
}
if (st.ffB0 || st.ffB1) {
SigSpec B = cell->getPort(ID(B));
if (st.ffB1) {
f(B, st.ffB1, st.ffB1cemux, st.ffBcepol, ID(CEB), st.ffB1rstmux, st.ffBrstpol, ID(RSTB));
cell->setParam(ID(B1REG), 1);
}
if (st.ffB0) {
f(B, st.ffB0, st.ffB0cemux, st.ffBcepol, ID(CEB), st.ffB0rstmux, st.ffBrstpol, ID(RSTB));
cell->setParam(ID(B0REG), 1);
}
pm.add_siguser(B, cell);
cell->setPort(ID(B), B);
}
if (st.ffD) {
SigSpec D = cell->getPort(ID(D));
f(D, st.ffD, st.ffDcemux, st.ffDcepol, ID(CED), st.ffDrstmux, st.ffDrstpol, ID(RSTD));
pm.add_siguser(D, cell);
cell->setPort(ID(D), D);
cell->setParam(ID(DREG), 1);
}
if (st.ffM) {
SigSpec M; // unused
f(M, st.ffM, st.ffMcemux, st.ffMcepol, ID(CEM), st.ffMrstmux, st.ffMrstpol, ID(RSTM));
st.ffM->connections_.at(ID(Q)).replace(st.sigM, pm.module->addWire(NEW_ID, GetSize(st.sigM)));
cell->setParam(ID(MREG), State::S1);
}
if (st.ffP) {
SigSpec P; // unused
f(P, st.ffP, st.ffPcemux, st.ffPcepol, ID(CEP), st.ffPrstmux, st.ffPrstpol, ID(RSTP));
st.ffP->connections_.at(ID(Q)).replace(st.sigP, pm.module->addWire(NEW_ID, GetSize(st.sigP)));
cell->setParam(ID(PREG), State::S1);
}
log(" clock: %s (%s)", log_signal(st.clock), "posedge");
if (st.ffA0)
log(" ffA0:%s", log_id(st.ffA0));
if (st.ffA1)
log(" ffA1:%s", log_id(st.ffA1));
if (st.ffB0)
log(" ffB0:%s", log_id(st.ffB0));
if (st.ffB1)
log(" ffB1:%s", log_id(st.ffB1));
if (st.ffD)
log(" ffD:%s", log_id(st.ffD));
if (st.ffM)
log(" ffM:%s", log_id(st.ffM));
if (st.ffP)
log(" ffP:%s", log_id(st.ffP));
}
log("\n");
SigSpec P = st.sigP;
if (GetSize(P) < 48)
P.append(pm.module->addWire(NEW_ID, 48-GetSize(P)));
cell->setPort(ID(P), P);
pm.blacklist(cell);
}
void xilinx_dsp_packC(xilinx_dsp_CREG_pm &pm) void xilinx_dsp_packC(xilinx_dsp_CREG_pm &pm)
{ {
auto &st = pm.st_xilinx_dsp_packC; auto &st = pm.st_xilinx_dsp_packC;
@ -592,33 +777,48 @@ struct XilinxDspPass : public Pass {
log("P output implementing the operation \"(P >= <power-of-2>)\" will be transformed\n"); log("P output implementing the operation \"(P >= <power-of-2>)\" will be transformed\n");
log("into using the DSP48E1's pattern detector feature for overflow detection.\n"); log("into using the DSP48E1's pattern detector feature for overflow detection.\n");
log("\n"); log("\n");
log(" -family {xcup|xcu|xc7|xc6v|xc5v|xc4v|xc6s|xc3sda}\n");
log(" select the family to target\n");
log(" default: xc7\n");
log("\n");
} }
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{ {
log_header(design, "Executing XILINX_DSP pass (pack resources into DSPs).\n"); log_header(design, "Executing XILINX_DSP pass (pack resources into DSPs).\n");
std::string family = "xc7";
size_t argidx; size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) for (argidx = 1; argidx < args.size(); argidx++)
{ {
// if (args[argidx] == "-singleton") { if ((args[argidx] == "-family" || args[argidx] == "-arch") && argidx+1 < args.size()) {
// singleton_mode = true; family = args[++argidx];
// continue; continue;
// } }
break; break;
} }
extra_args(args, argidx, design); extra_args(args, argidx, design);
// Don't bother distinguishing between those.
if (family == "xc6v")
family = "xc7";
if (family == "xcup")
family = "xcu";
for (auto module : design->selected_modules()) { for (auto module : design->selected_modules()) {
// Experimental feature: pack $add/$sub cells with // Experimental feature: pack $add/$sub cells with
// (* use_dsp48="simd" *) into DSP48E1's using its // (* use_dsp48="simd" *) into DSP48E1's using its
// SIMD feature // SIMD feature
if (family == "xc7")
xilinx_simd_pack(module, module->selected_cells()); xilinx_simd_pack(module, module->selected_cells());
// Match for all features ([ABDMP][12]?REG, pre-adder, // Match for all features ([ABDMP][12]?REG, pre-adder,
// post-adder, pattern detector, etc.) except for CREG // post-adder, pattern detector, etc.) except for CREG
{ if (family == "xc7") {
xilinx_dsp_pm pm(module, module->selected_cells()); xilinx_dsp_pm pm(module, module->selected_cells());
pm.run_xilinx_dsp_pack(xilinx_dsp_pack); pm.run_xilinx_dsp_pack(xilinx_dsp_pack);
} else if (family == "xc6s" || family == "xc3sda") {
xilinx_dsp48a_pm pm(module, module->selected_cells());
pm.run_xilinx_dsp48a_pack(xilinx_dsp48a_pack);
} }
// Separating out CREG packing is necessary since there // Separating out CREG packing is necessary since there
// is no guarantee that the cell ordering corresponds // is no guarantee that the cell ordering corresponds

View File

@ -0,0 +1,673 @@
// This file describes the main pattern matcher setup (of three total) that
// forms the `xilinx_dsp` pass described in xilinx_dsp.cc - version for
// DSP48A/DSP48A1 (Spartan 3A DSP, Spartan 6).
// At a high level, it works as follows:
// ( 1) Starting from a DSP48A/DSP48A1 cell
// ( 2) Match the driver of the 'B' input to a possible $dff cell (B1REG)
// (attached to at most two $mux cells that implement clock-enable or
// reset functionality, using a subpattern discussed below)
// If B1REG matched, treat 'B' input as input of B1REG
// ( 3) Match the driver of the 'B' and 'D' inputs for a possible $add cell
// (pre-adder)
// ( 4) Match 'B' input for B0REG
// ( 5) Match 'A' input for A1REG
// If A1REG, then match 'A' input for A0REG
// ( 6) Match 'D' input for DREG
// ( 7) Match 'P' output that exclusively drives an MREG
// ( 8) Match 'P' output that exclusively drives one of two inputs to an $add
// cell (post-adder).
// The other input to the adder is assumed to come in from the 'C' input
// (note: 'P' -> 'C' connections that exist for accumulators are
// recognised in xilinx_dsp.cc).
// ( 9) Match 'P' output that exclusively drives a PREG
// (10) If post-adder and PREG both present, match for a $mux cell driving
// the 'C' input, where one of the $mux's inputs is the PREG output.
// This indicates an accumulator situation, and one where a $mux exists
// to override the accumulated value:
// +--------------------------------+
// | ____ |
// +--| \ |
// |$mux|-+ |
// 'C' ---|____/ | |
// | /-------\ +----+ |
// +----+ +-| post- |___|PREG|---+ 'P'
// |MREG|------ | adder | +----+
// +----+ \-------/
// Notes: see the notes in xilinx_dsp.pmg
pattern xilinx_dsp48a_pack
state <SigBit> clock
state <SigSpec> sigA sigB sigC sigD sigM sigP
state <IdString> postAddAB postAddMuxAB
state <bool> ffAcepol ffBcepol ffDcepol ffMcepol ffPcepol
state <bool> ffArstpol ffBrstpol ffDrstpol ffMrstpol ffPrstpol
state <Cell*> ffA0 ffA0cemux ffA0rstmux ffA1 ffA1cemux ffA1rstmux
state <Cell*> ffB0 ffB0cemux ffB0rstmux ffB1 ffB1cemux ffB1rstmux
state <Cell*> ffD ffDcemux ffDrstmux ffM ffMcemux ffMrstmux ffP ffPcemux ffPrstmux
// Variables used for subpatterns
state <SigSpec> argQ argD
state <bool> ffcepol ffrstpol
state <int> ffoffset
udata <SigSpec> dffD dffQ
udata <SigBit> dffclock
udata <Cell*> dff dffcemux dffrstmux
udata <bool> dffcepol dffrstpol
// (1) Starting from a DSP48A/DSP48A1 cell
match dsp
select dsp->type.in(\DSP48A, \DSP48A1)
endmatch
code sigA sigB sigC sigD sigM clock
auto unextend = [](const SigSpec &sig) {
int i;
for (i = GetSize(sig)-1; i > 0; i--)
if (sig[i] != sig[i-1])
break;
// Do not remove non-const sign bit
if (sig[i].wire)
++i;
return sig.extract(0, i);
};
sigA = unextend(port(dsp, \A));
sigB = unextend(port(dsp, \B));
sigC = port(dsp, \C, SigSpec());
sigD = port(dsp, \D, SigSpec());
SigSpec P = port(dsp, \P);
// Only care about those bits that are used
int i;
for (i = GetSize(P)-1; i >= 0; i--)
if (nusers(P[i]) > 1)
break;
i++;
log_assert(nusers(P.extract_end(i)) <= 1);
// This sigM could have no users if downstream sinks (e.g. $add) is
// narrower than $mul result, for example
if (i == 0)
reject;
sigM = P.extract(0, i);
clock = port(dsp, \CLK, SigBit());
endcode
// (2) Match the driver of the 'B' input to a possible $dff cell (B1REG)
// (attached to at most two $mux cells that implement clock-enable or
// reset functionality, using a subpattern discussed above)
// If matched, treat 'B' input as input of B1REG
code argQ ffB1 ffB1cemux ffB1rstmux ffBcepol ffBrstpol sigB clock
if (param(dsp, \B1REG).as_int() == 0 && param(dsp, \B0REG).as_int() == 0 && port(dsp, \OPMODE, SigSpec()).extract(4, 1).is_fully_zero()) {
argQ = sigB;
subpattern(in_dffe);
if (dff) {
ffB1 = dff;
clock = dffclock;
if (dffrstmux) {
ffB1rstmux = dffrstmux;
ffBrstpol = dffrstpol;
}
if (dffcemux) {
ffB1cemux = dffcemux;
ffBcepol = dffcepol;
}
sigB = dffD;
}
}
endcode
// (3) Match the driver of the 'B' and 'D' inputs for a possible $add cell
// (pre-adder)
match preAdd
if sigD.empty() || sigD.is_fully_zero()
if param(dsp, \B0REG).as_int() == 0
// Ensure that preAdder not already used
if port(dsp, \OPMODE, SigSpec()).extract(4, 1).is_fully_zero()
select preAdd->type.in($add, $sub)
// Output has to be 18 bits or less
select GetSize(port(preAdd, \Y)) <= 18
select nusers(port(preAdd, \Y)) == 2
// D port has to be 18 bits or less
select GetSize(port(preAdd, \A)) <= 18
// B port has to be 18 bits or less
select GetSize(port(preAdd, \B)) <= 18
index <SigSpec> port(preAdd, \Y) === sigB
optional
endmatch
code sigB sigD
if (preAdd) {
sigD = port(preAdd, \A);
sigB = port(preAdd, \B);
}
endcode
// (4) Match 'B' input for B0REG
code argQ ffB0 ffB0cemux ffB0rstmux ffBcepol ffBrstpol sigB clock
if (param(dsp, \B0REG).as_int() == 0) {
argQ = sigB;
subpattern(in_dffe);
if (dff) {
if (ffB1) {
if ((ffB1rstmux != nullptr) ^ (dffrstmux != nullptr))
goto ffB0_end;
if ((ffB1cemux != nullptr) ^ (dffcemux != nullptr))
goto ffB0_end;
if (dffrstmux) {
if (ffBrstpol != dffrstpol)
goto ffB0_end;
if (port(ffB1rstmux, \S) != port(dffrstmux, \S))
goto ffB0_end;
ffB0rstmux = dffrstmux;
}
if (dffcemux) {
if (ffBcepol != dffcepol)
goto ffB0_end;
if (port(ffB1cemux, \S) != port(dffcemux, \S))
goto ffB0_end;
ffB0cemux = dffcemux;
}
}
ffB0 = dff;
clock = dffclock;
if (dffrstmux) {
ffB0rstmux = dffrstmux;
ffBrstpol = dffrstpol;
}
if (dffcemux) {
ffB0cemux = dffcemux;
ffBcepol = dffcepol;
}
sigB = dffD;
}
}
ffB0_end:
endcode
// (5) Match 'A' input for A1REG
// If A1REG, then match 'A' input for A0REG
code argQ ffA1 ffA1cemux ffA1rstmux ffAcepol ffArstpol sigA clock ffA0 ffA0cemux ffA0rstmux
if (param(dsp, \A0REG).as_int() == 0 && param(dsp, \A1REG).as_int() == 0) {
argQ = sigA;
subpattern(in_dffe);
if (dff) {
ffA1 = dff;
clock = dffclock;
if (dffrstmux) {
ffA1rstmux = dffrstmux;
ffArstpol = dffrstpol;
}
if (dffcemux) {
ffA1cemux = dffcemux;
ffAcepol = dffcepol;
}
sigA = dffD;
// Now attempt to match A0
if (ffA1) {
argQ = sigA;
subpattern(in_dffe);
if (dff) {
if ((ffA1rstmux != nullptr) ^ (dffrstmux != nullptr))
goto ffA0_end;
if ((ffA1cemux != nullptr) ^ (dffcemux != nullptr))
goto ffA0_end;
if (dffrstmux) {
if (ffArstpol != dffrstpol)
goto ffA0_end;
if (port(ffA1rstmux, \S) != port(dffrstmux, \S))
goto ffA0_end;
ffA0rstmux = dffrstmux;
}
if (dffcemux) {
if (ffAcepol != dffcepol)
goto ffA0_end;
if (port(ffA1cemux, \S) != port(dffcemux, \S))
goto ffA0_end;
ffA0cemux = dffcemux;
}
ffA0 = dff;
clock = dffclock;
if (dffcemux) {
ffA0cemux = dffcemux;
ffAcepol = dffcepol;
}
sigA = dffD;
ffA0_end: ;
}
}
}
}
endcode
// (6) Match 'D' input for DREG
code argQ ffD ffDcemux ffDrstmux ffDcepol ffDrstpol sigD clock
if (param(dsp, \DREG).as_int() == 0) {
argQ = sigD;
subpattern(in_dffe);
if (dff) {
ffD = dff;
clock = dffclock;
if (dffrstmux) {
ffDrstmux = dffrstmux;
ffDrstpol = dffrstpol;
}
if (dffcemux) {
ffDcemux = dffcemux;
ffDcepol = dffcepol;
}
sigD = dffD;
}
}
endcode
// (7) Match 'P' output that exclusively drives an MREG
code argD ffM ffMcemux ffMrstmux ffMcepol ffMrstpol sigM sigP clock
if (param(dsp, \MREG).as_int() == 0 && nusers(sigM) == 2) {
argD = sigM;
subpattern(out_dffe);
if (dff) {
ffM = dff;
clock = dffclock;
if (dffrstmux) {
ffMrstmux = dffrstmux;
ffMrstpol = dffrstpol;
}
if (dffcemux) {
ffMcemux = dffcemux;
ffMcepol = dffcepol;
}
sigM = dffQ;
}
}
sigP = sigM;
endcode
// (8) Match 'P' output that exclusively drives one of two inputs to an $add
// cell (post-adder).
// The other input to the adder is assumed to come in from the 'C' input
// (note: 'P' -> 'C' connections that exist for accumulators are
// recognised in xilinx_dsp.cc).
match postAdd
// Ensure that Z mux is not already used
if port(dsp, \OPMODE, SigSpec()).extract(2,2).is_fully_zero()
select postAdd->type.in($add)
select GetSize(port(postAdd, \Y)) <= 48
choice <IdString> AB {\A, \B}
select nusers(port(postAdd, AB)) <= 3
filter ffMcemux || nusers(port(postAdd, AB)) == 2
filter !ffMcemux || nusers(port(postAdd, AB)) == 3
index <SigBit> port(postAdd, AB)[0] === sigP[0]
filter GetSize(port(postAdd, AB)) >= GetSize(sigP)
filter port(postAdd, AB).extract(0, GetSize(sigP)) == sigP
// Check that remainder of AB is a sign- or zero-extension
filter port(postAdd, AB).extract_end(GetSize(sigP)) == SigSpec(sigP[GetSize(sigP)-1], GetSize(port(postAdd, AB))-GetSize(sigP)) || port(postAdd, AB).extract_end(GetSize(sigP)) == SigSpec(State::S0, GetSize(port(postAdd, AB))-GetSize(sigP))
set postAddAB AB
optional
endmatch
code sigC sigP
if (postAdd) {
sigC = port(postAdd, postAddAB == \A ? \B : \A);
sigP = port(postAdd, \Y);
}
endcode
// (9) Match 'P' output that exclusively drives a PREG
code argD ffP ffPcemux ffPrstmux ffPcepol ffPrstpol sigP clock
if (param(dsp, \PREG).as_int() == 0) {
int users = 2;
// If ffMcemux and no postAdd new-value net must have three users: ffMcemux, ffM and ffPcemux
if (ffMcemux && !postAdd) users++;
if (nusers(sigP) == users) {
argD = sigP;
subpattern(out_dffe);
if (dff) {
ffP = dff;
clock = dffclock;
if (dffrstmux) {
ffPrstmux = dffrstmux;
ffPrstpol = dffrstpol;
}
if (dffcemux) {
ffPcemux = dffcemux;
ffPcepol = dffcepol;
}
sigP = dffQ;
}
}
}
endcode
// (10) If post-adder and PREG both present, match for a $mux cell driving
// the 'C' input, where one of the $mux's inputs is the PREG output.
// This indicates an accumulator situation, and one where a $mux exists
// to override the accumulated value:
// +--------------------------------+
// | ____ |
// +--| \ |
// |$mux|-+ |
// 'C' ---|____/ | |
// | /-------\ +----+ |
// +----+ +-| post- |___|PREG|---+ 'P'
// |MREG|------ | adder | +----+
// +----+ \-------/
match postAddMux
if postAdd
if ffP
select postAddMux->type.in($mux)
select nusers(port(postAddMux, \Y)) == 2
choice <IdString> AB {\A, \B}
index <SigSpec> port(postAddMux, AB) === sigP
index <SigSpec> port(postAddMux, \Y) === sigC
set postAddMuxAB AB
optional
endmatch
code sigC
if (postAddMux)
sigC = port(postAddMux, postAddMuxAB == \A ? \B : \A);
endcode
code
accept;
endcode
// #######################
// Subpattern for matching against input registers, based on knowledge of the
// 'Q' input. Typically, identifying registers with clock-enable and reset
// capability would be a task would be handled by other Yosys passes such as
// dff2dffe, but since DSP inference happens much before this, these patterns
// have to be manually identified.
// At a high level:
// (1) Starting from a $dff cell that (partially or fully) drives the given
// 'Q' argument
// (2) Match for a $mux cell implementing synchronous reset semantics ---
// one that exclusively drives the 'D' input of the $dff, with one of its
// $mux inputs being fully zero
// (3) Match for a $mux cell implement clock enable semantics --- one that
// exclusively drives the 'D' input of the $dff (or the other input of
// the reset $mux) and where one of this $mux's inputs is connected to
// the 'Q' output of the $dff
subpattern in_dffe
arg argD argQ clock
code
dff = nullptr;
if (GetSize(argQ) == 0)
reject;
for (const auto &c : argQ.chunks()) {
// Abandon matches when 'Q' is a constant
if (!c.wire)
reject;
// Abandon matches when 'Q' has the keep attribute set
if (c.wire->get_bool_attribute(\keep))
reject;
// Abandon matches when 'Q' has a non-zero init attribute set
// (not supported by DSP48E1)
Const init = c.wire->attributes.at(\init, Const());
if (!init.empty())
for (auto b : init.extract(c.offset, c.width))
if (b != State::Sx && b != State::S0)
reject;
}
endcode
// (1) Starting from a $dff cell that (partially or fully) drives the given
// 'Q' argument
match ff
select ff->type.in($dff)
// DSP48E1 does not support clock inversion
select param(ff, \CLK_POLARITY).as_bool()
slice offset GetSize(port(ff, \D))
index <SigBit> port(ff, \Q)[offset] === argQ[0]
// Check that the rest of argQ is present
filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
filter clock == SigBit() || port(ff, \CLK) == clock
set ffoffset offset
endmatch
code argQ argD
SigSpec Q = port(ff, \Q);
dff = ff;
dffclock = port(ff, \CLK);
dffD = argQ;
argD = port(ff, \D);
argQ = Q;
dffD.replace(argQ, argD);
// Only search for ffrstmux if dffD only
// has two (ff, ffrstmux) users
if (nusers(dffD) > 2)
argD = SigSpec();
endcode
// (2) Match for a $mux cell implementing synchronous reset semantics ---
// exclusively drives the 'D' input of the $dff, with one of the $mux
// inputs being fully zero
match ffrstmux
if !argD.empty()
select ffrstmux->type.in($mux)
index <SigSpec> port(ffrstmux, \Y) === argD
choice <IdString> BA {\B, \A}
// DSP48E1 only supports reset to zero
select port(ffrstmux, BA).is_fully_zero()
define <bool> pol (BA == \B)
set ffrstpol pol
semioptional
endmatch
code argD
if (ffrstmux) {
dffrstmux = ffrstmux;
dffrstpol = ffrstpol;
argD = port(ffrstmux, ffrstpol ? \A : \B);
dffD.replace(port(ffrstmux, \Y), argD);
// Only search for ffcemux if argQ has at
// least 3 users (ff, <upstream>, ffrstmux) and
// dffD only has two (ff, ffrstmux)
if (!(nusers(argQ) >= 3 && nusers(dffD) == 2))
argD = SigSpec();
}
else
dffrstmux = nullptr;
endcode
// (3) Match for a $mux cell implement clock enable semantics --- one that
// exclusively drives the 'D' input of the $dff (or the other input of
// the reset $mux) and where one of this $mux's inputs is connected to
// the 'Q' output of the $dff
match ffcemux
if !argD.empty()
select ffcemux->type.in($mux)
index <SigSpec> port(ffcemux, \Y) === argD
choice <IdString> AB {\A, \B}
index <SigSpec> port(ffcemux, AB) === argQ
define <bool> pol (AB == \A)
set ffcepol pol
semioptional
endmatch
code argD
if (ffcemux) {
dffcemux = ffcemux;
dffcepol = ffcepol;
argD = port(ffcemux, ffcepol ? \B : \A);
dffD.replace(port(ffcemux, \Y), argD);
}
else
dffcemux = nullptr;
endcode
// #######################
// Subpattern for matching against output registers, based on knowledge of the
// 'D' input.
// At a high level:
// (1) Starting from an optional $mux cell that implements clock enable
// semantics --- one where the given 'D' argument (partially or fully)
// drives one of its two inputs
// (2) Starting from, or continuing onto, another optional $mux cell that
// implements synchronous reset semantics --- one where the given 'D'
// argument (or the clock enable $mux output) drives one of its two inputs
// and where the other input is fully zero
// (3) Match for a $dff cell (whose 'D' input is the 'D' argument, or the
// output of the previous clock enable or reset $mux cells)
subpattern out_dffe
arg argD argQ clock
code
dff = nullptr;
for (auto c : argD.chunks())
// Abandon matches when 'D' has the keep attribute set
if (c.wire->get_bool_attribute(\keep))
reject;
endcode
// (1) Starting from an optional $mux cell that implements clock enable
// semantics --- one where the given 'D' argument (partially or fully)
// drives one of its two inputs
match ffcemux
select ffcemux->type.in($mux)
// ffcemux output must have two users: ffcemux and ff.D
select nusers(port(ffcemux, \Y)) == 2
choice <IdString> AB {\A, \B}
// keep-last-value net must have at least three users: ffcemux, ff, downstream sink(s)
select nusers(port(ffcemux, AB)) >= 3
slice offset GetSize(port(ffcemux, \Y))
define <IdString> BA (AB == \A ? \B : \A)
index <SigBit> port(ffcemux, BA)[offset] === argD[0]
// Check that the rest of argD is present
filter GetSize(port(ffcemux, BA)) >= offset + GetSize(argD)
filter port(ffcemux, BA).extract(offset, GetSize(argD)) == argD
set ffoffset offset
define <bool> pol (AB == \A)
set ffcepol pol
semioptional
endmatch
code argD argQ
dffcemux = ffcemux;
if (ffcemux) {
SigSpec BA = port(ffcemux, ffcepol ? \B : \A);
SigSpec Y = port(ffcemux, \Y);
argQ = argD;
argD.replace(BA, Y);
argQ.replace(BA, port(ffcemux, ffcepol ? \A : \B));
dffcemux = ffcemux;
dffcepol = ffcepol;
}
endcode
// (2) Starting from, or continuing onto, another optional $mux cell that
// implements synchronous reset semantics --- one where the given 'D'
// argument (or the clock enable $mux output) drives one of its two inputs
// and where the other input is fully zero
match ffrstmux
select ffrstmux->type.in($mux)
// ffrstmux output must have two users: ffrstmux and ff.D
select nusers(port(ffrstmux, \Y)) == 2
choice <IdString> BA {\B, \A}
// DSP48E1 only supports reset to zero
select port(ffrstmux, BA).is_fully_zero()
slice offset GetSize(port(ffrstmux, \Y))
define <IdString> AB (BA == \B ? \A : \B)
index <SigBit> port(ffrstmux, AB)[offset] === argD[0]
// Check that offset is consistent
filter !ffcemux || ffoffset == offset
// Check that the rest of argD is present
filter GetSize(port(ffrstmux, AB)) >= offset + GetSize(argD)
filter port(ffrstmux, AB).extract(offset, GetSize(argD)) == argD
set ffoffset offset
define <bool> pol (AB == \A)
set ffrstpol pol
semioptional
endmatch
code argD argQ
dffrstmux = ffrstmux;
if (ffrstmux) {
SigSpec AB = port(ffrstmux, ffrstpol ? \A : \B);
SigSpec Y = port(ffrstmux, \Y);
argD.replace(AB, Y);
dffrstmux = ffrstmux;
dffrstpol = ffrstpol;
}
endcode
// (3) Match for a $dff cell (whose 'D' input is the 'D' argument, or the
// output of the previous clock enable or reset $mux cells)
match ff
select ff->type.in($dff)
// DSP48E1 does not support clock inversion
select param(ff, \CLK_POLARITY).as_bool()
slice offset GetSize(port(ff, \D))
index <SigBit> port(ff, \D)[offset] === argD[0]
// Check that offset is consistent
filter (!ffcemux && !ffrstmux) || ffoffset == offset
// Check that the rest of argD is present
filter GetSize(port(ff, \D)) >= offset + GetSize(argD)
filter port(ff, \D).extract(offset, GetSize(argD)) == argD
// Check that FF.Q is connected to CE-mux
filter !ffcemux || port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
filter clock == SigBit() || port(ff, \CLK) == clock
set ffoffset offset
endmatch
code argQ
SigSpec D = port(ff, \D);
SigSpec Q = port(ff, \Q);
if (!ffcemux) {
argQ = argD;
argQ.replace(D, Q);
}
// Abandon matches when 'Q' has a non-zero init attribute set
// (not supported by DSP48E1)
for (auto c : argQ.chunks()) {
Const init = c.wire->attributes.at(\init, Const());
if (!init.empty())
for (auto b : init.extract(c.offset, c.width))
if (b != State::Sx && b != State::S0)
reject;
}
dff = ff;
dffQ = argQ;
dffclock = port(ff, \CLK);
endcode

View File

@ -1,7 +1,7 @@
// This file describes the second of three pattern matcher setups that // This file describes the second of three pattern matcher setups that
// forms the `xilinx_dsp` pass described in xilinx_dsp.cc // forms the `xilinx_dsp` pass described in xilinx_dsp.cc
// At a high level, it works as follows: // At a high level, it works as follows:
// (1) Starting from a DSP48E1 cell that (a) doesn't have a CREG already, // (1) Starting from a DSP48* cell that (a) doesn't have a CREG already,
// and (b) uses the 'C' port // and (b) uses the 'C' port
// (2) Match the driver of the 'C' input to a possible $dff cell (CREG) // (2) Match the driver of the 'C' input to a possible $dff cell (CREG)
// (attached to at most two $mux cells that implement clock-enable or // (attached to at most two $mux cells that implement clock-enable or
@ -38,10 +38,10 @@ udata <SigBit> dffclock
udata <Cell*> dff dffcemux dffrstmux udata <Cell*> dff dffcemux dffrstmux
udata <bool> dffcepol dffrstpol udata <bool> dffcepol dffrstpol
// (1) Starting from a DSP48E1 cell that (a) doesn't have a CREG already, // (1) Starting from a DSP48* cell that (a) doesn't have a CREG already,
// and (b) uses the 'C' port // and (b) uses the 'C' port
match dsp match dsp
select dsp->type.in(\DSP48E1) select dsp->type.in(\DSP48A, \DSP48A1, \DSP48E1)
select param(dsp, \CREG, 1).as_int() == 0 select param(dsp, \CREG, 1).as_int() == 0
select nusers(port(dsp, \C, SigSpec())) > 1 select nusers(port(dsp, \C, SigSpec())) > 1
endmatch endmatch
@ -60,7 +60,8 @@ code sigC sigP clock
sigC = unextend(port(dsp, \C, SigSpec())); sigC = unextend(port(dsp, \C, SigSpec()));
SigSpec P = port(dsp, \P); SigSpec P = port(dsp, \P);
if (param(dsp, \USE_MULT, Const("MULTIPLY")).decode_string() == "MULTIPLY") { if (!dsp->type.in(\DSP48E1) ||
param(dsp, \USE_MULT, Const("MULTIPLY")).decode_string() == "MULTIPLY") {
// Only care about those bits that are used // Only care about those bits that are used
int i; int i;
for (i = GetSize(P)-1; i >= 0; i--) for (i = GetSize(P)-1; i >= 0; i--)

View File

@ -62,12 +62,11 @@ code
#define MAX_DSP_CASCADE 20 #define MAX_DSP_CASCADE 20
endcode endcode
// (1) Starting from a DSP48E1 cell that (a) has the Z multiplexer // (1) Starting from a DSP48* cell that (a) has the Z multiplexer
// (controlled by OPMODE[6:4]) set to zero and (b) doesn't already // (controlled by OPMODE[3:2] for DSP48A*, by OPMODE[6:4] for DSP48E1)
// use the 'PCOUT' port // set to zero and (b) doesn't already use the 'PCOUT' port
match first match first
select first->type.in(\DSP48E1) select (first->type.in(\DSP48A, \DSP48A1) && port(first, \OPMODE, Const(0, 8)).extract(2,2) == Const::from_string("00")) || (first->type.in(\DSP48E1) && port(first, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("000"))
select port(first, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("000")
select nusers(port(first, \PCOUT, SigSpec())) <= 1 select nusers(port(first, \PCOUT, SigSpec())) <= 1
endmatch endmatch
@ -100,6 +99,12 @@ finally
add_siguser(cascade, dsp); add_siguser(cascade, dsp);
SigSpec opmode = port(dsp_pcin, \OPMODE, Const(0, 7)); SigSpec opmode = port(dsp_pcin, \OPMODE, Const(0, 7));
if (dsp->type.in(\DSP48A, \DSP48A1)) {
log_assert(P == 0);
opmode[3] = State::S0;
opmode[2] = State::S1;
}
else if (dsp->type.in(\DSP48E1)) {
if (P == 17) if (P == 17)
opmode[6] = State::S1; opmode[6] = State::S1;
else if (P == 0) else if (P == 0)
@ -108,6 +113,7 @@ finally
opmode[5] = State::S0; opmode[5] = State::S0;
opmode[4] = State::S1; opmode[4] = State::S1;
}
dsp_pcin->setPort(\OPMODE, opmode); dsp_pcin->setPort(\OPMODE, opmode);
log_debug("PCOUT -> PCIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin)); log_debug("PCOUT -> PCIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin));
@ -120,6 +126,7 @@ finally
add_siguser(cascade, dsp_pcin); add_siguser(cascade, dsp_pcin);
add_siguser(cascade, dsp); add_siguser(cascade, dsp);
if (dsp->type.in(\DSP48E1))
dsp->setParam(ID(ACASCREG), AREG); dsp->setParam(ID(ACASCREG), AREG);
dsp_pcin->setParam(ID(A_INPUT), Const("CASCADE")); dsp_pcin->setParam(ID(A_INPUT), Const("CASCADE"));
@ -127,14 +134,34 @@ finally
} }
if (BREG >= 0) { if (BREG >= 0) {
Wire *cascade = module->addWire(NEW_ID, 18); Wire *cascade = module->addWire(NEW_ID, 18);
if (dsp->type.in(\DSP48A, \DSP48A1)) {
// According to UG389 p9 [https://www.xilinx.com/support/documentation/user_guides/ug389.pdf]
// "The DSP48A1 component uses this input when cascading
// BCOUT from an adjacent DSP48A1 slice. The tools then
// translate BCOUT cascading to the dedicated BCIN input
// and set the B_INPUT attribute for implementation."
dsp_pcin->setPort(ID(B), cascade);
}
else {
dsp_pcin->setPort(ID(B), Const(0, 18)); dsp_pcin->setPort(ID(B), Const(0, 18));
dsp_pcin->setPort(ID(BCIN), cascade); dsp_pcin->setPort(ID(BCIN), cascade);
}
dsp->setPort(ID(BCOUT), cascade); dsp->setPort(ID(BCOUT), cascade);
add_siguser(cascade, dsp_pcin); add_siguser(cascade, dsp_pcin);
add_siguser(cascade, dsp); add_siguser(cascade, dsp);
if (dsp->type.in(\DSP48E1)) {
dsp->setParam(ID(BCASCREG), BREG); dsp->setParam(ID(BCASCREG), BREG);
// According to UG389 p13 [https://www.xilinx.com/support/documentation/user_guides/ug389.pdf]
// "The attribute is only used by place and route tools and
// is not necessary for the users to set for synthesis. The
// attribute is determined by the connection to the B port
// of the DSP48A1 slice. If the B port is connected to the
// BCOUT of another DSP48A1 slice, then the tools automatically
// set the attribute to 'CASCADE', otherwise it is set to
// 'DIRECT'".
dsp_pcin->setParam(ID(B_INPUT), Const("CASCADE")); dsp_pcin->setParam(ID(B_INPUT), Const("CASCADE"));
}
log_debug("BCOUT -> BCIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin)); log_debug("BCOUT -> BCIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin));
} }
@ -156,22 +183,21 @@ subpattern tail
arg first arg first
arg next arg next
// (2.1) Match another DSP48E1 cell that (a) does not have the CREG enabled, // (2.1) Match another DSP48* cell that (a) does not have the CREG enabled,
// (b) has its Z multiplexer output set to the 'C' port, which is // (b) has its Z multiplexer output set to the 'C' port, which is
// driven by the 'P' output of the previous DSP cell, and (c) has its // driven by the 'P' output of the previous DSP cell, and (c) has its
// 'PCIN' port unused // 'PCIN' port unused
match nextP match nextP
select nextP->type.in(\DSP48E1)
select !param(nextP, \CREG, State::S1).as_bool() select !param(nextP, \CREG, State::S1).as_bool()
select port(nextP, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("011") select (nextP->type.in(\DSP48A, \DSP48A1) && port(nextP, \OPMODE, Const(0, 8)).extract(2,2) == Const::from_string("11")) || (nextP->type.in(\DSP48E1) && port(nextP, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("011"))
select nusers(port(nextP, \C, SigSpec())) > 1 select nusers(port(nextP, \C, SigSpec())) > 1
select nusers(port(nextP, \PCIN, SigSpec())) == 0 select nusers(port(nextP, \PCIN, SigSpec())) == 0
index <SigBit> port(nextP, \C)[0] === port(std::get<0>(chain.back()), \P)[0] index <SigBit> port(nextP, \C)[0] === port(std::get<0>(chain.back()), \P)[0]
semioptional semioptional
endmatch endmatch
// (2.2) Same as (2.1) but with the 'C' port driven by the 'P' output of the // (2.2) For DSP48E1 only, same as (2.1) but with the 'C' port driven
// previous DSP cell right-shifted by 17 bits // by the 'P' output of the previous DSP cell right-shifted by 17 bits
match nextP_shift17 match nextP_shift17
if !nextP if !nextP
select nextP_shift17->type.in(\DSP48E1) select nextP_shift17->type.in(\DSP48E1)
@ -188,6 +214,8 @@ code next
if (!nextP) if (!nextP)
next = nextP_shift17; next = nextP_shift17;
if (next) { if (next) {
if (next->type != first->type)
reject;
unextend = [](const SigSpec &sig) { unextend = [](const SigSpec &sig) {
int i; int i;
for (i = GetSize(sig)-1; i > 0; i--) for (i = GetSize(sig)-1; i > 0; i--)
@ -202,21 +230,26 @@ code next
endcode endcode
// (3) For this subequent DSP48E1 match (i.e. PCOUT -> PCIN cascade exists) // (3) For this subequent DSP48E1 match (i.e. PCOUT -> PCIN cascade exists)
// if (a) the previous DSP48E1 uses either the A2REG or A1REG, (b) this // if (a) this DSP48E1 does not already have an ACOUT -> ACIN cascade,
// DSP48 does not use A2REG nor A1REG, (c) this DSP48E1 does not already // (b) the previous DSP does not already use its ACOUT port, then
// have an ACOUT -> ACIN cascade, (d) the previous DSP does not already // examine if an ACOUT -> ACIN cascade opportunity exists if
// use its ACOUT port, then examine if an ACOUT -> ACIN cascade // (i) A ports are identical, or (ii) separated by a
// opportunity exists by matching for a $dff-with-optional-clock-enable- // $dff-with-optional-clock-enable-or-reset and checking that the 'D' input
// or-reset and checking that the 'D' input of this register is the same // of this register is the same as the 'A' input of the previous DSP
// as the 'A' input of the previous DSP // TODO: Check for two levels of flops, instead of just one
code argQ clock AREG code argQ clock AREG
AREG = -1; AREG = -1;
if (next) { if (next && next->type.in(\DSP48E1)) {
Cell *prev = std::get<0>(chain.back()); Cell *prev = std::get<0>(chain.back());
if (param(prev, \AREG, 2).as_int() > 0 &&
param(next, \AREG, 2).as_int() > 0 && if (param(next, \A_INPUT, Const("DIRECT")).decode_string() == "DIRECT" &&
param(next, \A_INPUT, Const("DIRECT")).decode_string() == "DIRECT" && port(next, \ACIN, SigSpec()).is_fully_zero() &&
nusers(port(prev, \ACOUT, SigSpec())) <= 1) { nusers(port(prev, \ACOUT, SigSpec())) <= 1) {
if (param(prev, \AREG, 2) == 0) {
if (port(prev, \A) == port(next, \A))
AREG = 0;
}
else {
argQ = unextend(port(next, \A)); argQ = unextend(port(next, \A));
clock = port(prev, \CLK); clock = port(prev, \CLK);
subpattern(in_dffe); subpattern(in_dffe);
@ -225,16 +258,23 @@ code argQ clock AREG
goto reject_AREG; goto reject_AREG;
if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTA, State::S0)) if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTA, State::S0))
goto reject_AREG; goto reject_AREG;
if (!dffcemux && port(prev, \CEA2, State::S0) != State::S0) IdString CEA;
if (param(prev, \AREG, 2) == 1)
CEA = \CEA2;
else if (param(prev, \AREG, 2) == 2)
CEA = \CEA1;
else log_abort();
if (!dffcemux && port(prev, CEA, State::S0) != State::S1)
goto reject_AREG; goto reject_AREG;
if (dffcemux && port(dffcemux, \S) != port(prev, \CEA2, State::S0)) if (dffcemux && port(dffcemux, \S) != port(prev, CEA, State::S0))
goto reject_AREG; goto reject_AREG;
if (dffD == unextend(port(prev, \A))) if (dffD == unextend(port(prev, \A)))
AREG = 1; AREG = 1;
}
}
}
reject_AREG: ; reject_AREG: ;
} }
}
}
endcode endcode
// (4) Same as (3) but for BCOUT -> BCIN cascade // (4) Same as (3) but for BCOUT -> BCIN cascade
@ -242,11 +282,15 @@ code argQ clock BREG
BREG = -1; BREG = -1;
if (next) { if (next) {
Cell *prev = std::get<0>(chain.back()); Cell *prev = std::get<0>(chain.back());
if (param(prev, \BREG, 2).as_int() > 0 && if (param(next, \B_INPUT, Const("DIRECT")).decode_string() == "DIRECT" &&
param(next, \BREG, 2).as_int() > 0 &&
param(next, \B_INPUT, Const("DIRECT")).decode_string() == "DIRECT" &&
port(next, \BCIN, SigSpec()).is_fully_zero() && port(next, \BCIN, SigSpec()).is_fully_zero() &&
nusers(port(prev, \BCOUT, SigSpec())) <= 1) { nusers(port(prev, \BCOUT, SigSpec())) <= 1) {
if ((next->type.in(\DSP48A, \DSP48A1) && param(prev, \B0REG, 0) == 0 && param(prev, \B1REG, 1) == 0) ||
(next->type.in(\DSP48E1) && param(prev, \BREG, 2) == 0)) {
if (port(prev, \B) == port(next, \B))
BREG = 0;
}
else {
argQ = unextend(port(next, \B)); argQ = unextend(port(next, \B));
clock = port(prev, \CLK); clock = port(prev, \CLK);
subpattern(in_dffe); subpattern(in_dffe);
@ -255,16 +299,31 @@ code argQ clock BREG
goto reject_BREG; goto reject_BREG;
if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTB, State::S0)) if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTB, State::S0))
goto reject_BREG; goto reject_BREG;
if (!dffcemux && port(prev, \CEB2, State::S0) != State::S0) IdString CEB;
if (next->type.in(\DSP48A, \DSP48A1))
CEB = \CEB;
else if (next->type.in(\DSP48E1)) {
if (param(prev, \BREG, 2) == 1)
CEB = \CEB2;
else if (param(prev, \BREG, 2) == 2)
CEB = \CEB1;
else log_abort();
}
else log_abort();
if (!dffcemux && port(prev, CEB, State::S0) != State::S1)
goto reject_BREG; goto reject_BREG;
if (dffcemux && port(dffcemux, \S) != port(prev, \CEB2, State::S0)) if (dffcemux && port(dffcemux, \S) != port(prev, CEB, State::S0))
goto reject_BREG;
if (dffD == unextend(port(prev, \B))) {
if (next->type.in(\DSP48A, \DSP48A1) && param(prev, \B0REG, 0) != 0)
goto reject_BREG; goto reject_BREG;
if (dffD == unextend(port(prev, \B)))
BREG = 1; BREG = 1;
}
}
}
}
reject_BREG: ; reject_BREG: ;
} }
}
}
endcode endcode
// (5) Recursively go to (2.1) until no more matches possible, recording the // (5) Recursively go to (2.1) until no more matches possible, recording the

View File

@ -192,11 +192,28 @@ struct IopadmapPass : public Pass {
if (!toutpad_celltype.empty() || !tinoutpad_celltype.empty()) if (!toutpad_celltype.empty() || !tinoutpad_celltype.empty())
{ {
dict<SigBit, Cell *> tbuf_bits; dict<SigBit, Cell *> tbuf_bits;
pool<SigBit> driven_bits;
// Gather tristate buffers and always-on drivers.
for (auto cell : module->cells()) for (auto cell : module->cells())
if (cell->type == ID($_TBUF_)) { if (cell->type == ID($_TBUF_)) {
SigBit bit = cell->getPort(ID::Y).as_bit(); SigBit bit = cell->getPort(ID::Y).as_bit();
tbuf_bits[bit] = cell; tbuf_bits[bit] = cell;
} else {
for (auto port : cell->connections())
if (!cell->known() || cell->output(port.first))
for (auto bit : port.second)
driven_bits.insert(bit);
}
// If a wire is a target of an assignment, it is driven, unless the source is 'z.
for (auto &conn : module->connections())
for (int i = 0; i < GetSize(conn.first); i++) {
SigBit dstbit = conn.first[i];
SigBit srcbit = conn.second[i];
if (!srcbit.wire && srcbit.data == State::Sz)
continue;
driven_bits.insert(dstbit);
} }
for (auto wire : module->selected_wires()) for (auto wire : module->selected_wires())
@ -204,41 +221,68 @@ struct IopadmapPass : public Pass {
if (!wire->port_output) if (!wire->port_output)
continue; continue;
// Don't handle inout ports if we have no suitable buffer type.
if (wire->port_input && tinoutpad_celltype.empty())
continue;
// likewise for output ports.
if (!wire->port_input && toutpad_celltype.empty())
continue;
for (int i = 0; i < GetSize(wire); i++) for (int i = 0; i < GetSize(wire); i++)
{ {
SigBit wire_bit(wire, i); SigBit wire_bit(wire, i);
Cell *tbuf_cell = nullptr;
if (tbuf_bits.count(wire_bit) == 0) if (tbuf_bits.count(wire_bit))
tbuf_cell = tbuf_bits.at(wire_bit);
SigBit en_sig;
SigBit data_sig;
bool is_driven = driven_bits.count(wire_bit);
if (tbuf_cell != nullptr) {
// Found a tristate buffer — use it.
en_sig = tbuf_cell->getPort(ID(E)).as_bit();
data_sig = tbuf_cell->getPort(ID::A).as_bit();
} else if (is_driven) {
// No tristate buffer, but an always-on driver is present.
// If this is an inout port, we're creating a tinoutpad
// anyway, just with a constant 1 as enable.
if (!wire->port_input)
continue; continue;
en_sig = SigBit(State::S1);
data_sig = wire_bit;
} else {
// No driver on a wire. Create a tristate pad with always-0
// enable.
en_sig = SigBit(State::S0);
data_sig = SigBit(State::Sx);
}
Cell *tbuf_cell = tbuf_bits.at(wire_bit); if (wire->port_input)
if (tbuf_cell == nullptr)
continue;
SigBit en_sig = tbuf_cell->getPort(ID(E)).as_bit();
SigBit data_sig = tbuf_cell->getPort(ID::A).as_bit();
if (wire->port_input && !tinoutpad_celltype.empty())
{ {
log("Mapping port %s.%s[%d] using %s.\n", log_id(module), log_id(wire), i, tinoutpad_celltype.c_str()); log("Mapping port %s.%s[%d] using %s.\n", log_id(module), log_id(wire), i, tinoutpad_celltype.c_str());
Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(tinoutpad_celltype)); Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(tinoutpad_celltype));
cell->setPort(RTLIL::escape_id(tinoutpad_portname_oe), en_sig); cell->setPort(RTLIL::escape_id(tinoutpad_portname_oe), en_sig);
cell->setPort(RTLIL::escape_id(tinoutpad_portname_o), wire_bit);
cell->setPort(RTLIL::escape_id(tinoutpad_portname_i), data_sig);
cell->attributes[ID::keep] = RTLIL::Const(1); cell->attributes[ID::keep] = RTLIL::Const(1);
if (tbuf_cell) {
module->remove(tbuf_cell); module->remove(tbuf_cell);
cell->setPort(RTLIL::escape_id(tinoutpad_portname_o), wire_bit);
cell->setPort(RTLIL::escape_id(tinoutpad_portname_i), data_sig);
} else if (is_driven) {
cell->setPort(RTLIL::escape_id(tinoutpad_portname_i), wire_bit);
} else {
cell->setPort(RTLIL::escape_id(tinoutpad_portname_o), wire_bit);
cell->setPort(RTLIL::escape_id(tinoutpad_portname_i), data_sig);
}
skip_wire_bits.insert(wire_bit); skip_wire_bits.insert(wire_bit);
if (!tinoutpad_portname_pad.empty()) if (!tinoutpad_portname_pad.empty())
rewrite_bits[wire][i] = make_pair(cell, RTLIL::escape_id(tinoutpad_portname_pad)); rewrite_bits[wire][i] = make_pair(cell, RTLIL::escape_id(tinoutpad_portname_pad));
continue; } else {
}
if (!wire->port_input && !toutpad_celltype.empty())
{
log("Mapping port %s.%s[%d] using %s.\n", log_id(module), log_id(wire), i, toutpad_celltype.c_str()); log("Mapping port %s.%s[%d] using %s.\n", log_id(module), log_id(wire), i, toutpad_celltype.c_str());
Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(toutpad_celltype)); Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(toutpad_celltype));
@ -247,12 +291,13 @@ struct IopadmapPass : public Pass {
cell->setPort(RTLIL::escape_id(toutpad_portname_i), data_sig); cell->setPort(RTLIL::escape_id(toutpad_portname_i), data_sig);
cell->attributes[ID::keep] = RTLIL::Const(1); cell->attributes[ID::keep] = RTLIL::Const(1);
if (tbuf_cell) {
module->remove(tbuf_cell); module->remove(tbuf_cell);
module->connect(wire_bit, data_sig); module->connect(wire_bit, data_sig);
}
skip_wire_bits.insert(wire_bit); skip_wire_bits.insert(wire_bit);
if (!toutpad_portname_pad.empty()) if (!toutpad_portname_pad.empty())
rewrite_bits[wire][i] = make_pair(cell, RTLIL::escape_id(toutpad_portname_pad)); rewrite_bits[wire][i] = make_pair(cell, RTLIL::escape_id(toutpad_portname_pad));
continue;
} }
} }
} }

View File

@ -1,5 +1,6 @@
// --------------------------------------- // ---------------------------------------
(* lib_whitebox *)
module LUT4(input A, B, C, D, output Z); module LUT4(input A, B, C, D, output Z);
parameter [15:0] INIT = 16'h0000; parameter [15:0] INIT = 16'h0000;
wire [7:0] s3 = D ? INIT[15:8] : INIT[7:0]; wire [7:0] s3 = D ? INIT[15:8] : INIT[7:0];
@ -31,13 +32,8 @@ module CCU2C(
// First half // First half
wire LUT4_0, LUT2_0; wire LUT4_0, LUT2_0;
`ifdef _ABC
assign LUT4_0 = INIT0[{D0, C0, B0, A0}];
assign LUT2_0 = INIT0[{2'b00, B0, A0}];
`else
LUT4 #(.INIT(INIT0)) lut4_0(.A(A0), .B(B0), .C(C0), .D(D0), .Z(LUT4_0)); LUT4 #(.INIT(INIT0)) lut4_0(.A(A0), .B(B0), .C(C0), .D(D0), .Z(LUT4_0));
LUT2 #(.INIT(INIT0[3:0])) lut2_0(.A(A0), .B(B0), .Z(LUT2_0)); LUT2 #(.INIT(INIT0[3:0])) lut2_0(.A(A0), .B(B0), .Z(LUT2_0));
`endif
wire gated_cin_0 = (INJECT1_0 == "YES") ? 1'b0 : CIN; wire gated_cin_0 = (INJECT1_0 == "YES") ? 1'b0 : CIN;
assign S0 = LUT4_0 ^ gated_cin_0; assign S0 = LUT4_0 ^ gated_cin_0;
@ -46,13 +42,8 @@ module CCU2C(
// Second half // Second half
wire LUT4_1, LUT2_1; wire LUT4_1, LUT2_1;
`ifdef _ABC
assign LUT4_1 = INIT1[{D1, C1, B1, A1}];
assign LUT2_1 = INIT1[{2'b00, B1, A1}];
`else
LUT4 #(.INIT(INIT1)) lut4_1(.A(A1), .B(B1), .C(C1), .D(D1), .Z(LUT4_1)); LUT4 #(.INIT(INIT1)) lut4_1(.A(A1), .B(B1), .C(C1), .D(D1), .Z(LUT4_1));
LUT2 #(.INIT(INIT1[3:0])) lut2_1(.A(A1), .B(B1), .Z(LUT2_1)); LUT2 #(.INIT(INIT1[3:0])) lut2_1(.A(A1), .B(B1), .Z(LUT2_1));
`endif
wire gated_cin_1 = (INJECT1_1 == "YES") ? 1'b0 : cout_0; wire gated_cin_1 = (INJECT1_1 == "YES") ? 1'b0 : cout_0;
assign S1 = LUT4_1 ^ gated_cin_1; assign S1 = LUT4_1 ^ gated_cin_1;
@ -209,6 +200,7 @@ endmodule
// --------------------------------------- // ---------------------------------------
(* lib_whitebox *)
module LUT2(input A, B, output Z); module LUT2(input A, B, output Z);
parameter [3:0] INIT = 4'h0; parameter [3:0] INIT = 4'h0;
wire [1:0] s1 = B ? INIT[ 3:2] : INIT[1:0]; wire [1:0] s1 = B ? INIT[ 3:2] : INIT[1:0];

View File

@ -230,7 +230,7 @@ struct SynthEcp5Pass : public ScriptPass
{ {
if (check_label("begin")) if (check_label("begin"))
{ {
run("read_verilog -D_ABC -lib +/ecp5/cells_sim.v +/ecp5/cells_bb.v"); run("read_verilog -lib +/ecp5/cells_sim.v +/ecp5/cells_bb.v");
run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str())); run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str()));
} }

View File

@ -2107,7 +2107,7 @@ always @* begin
2'b00: XMUX <= 0; 2'b00: XMUX <= 0;
2'b01: XMUX <= M; 2'b01: XMUX <= M;
2'b10: XMUX <= P; 2'b10: XMUX <= P;
2'b11: XMUX <= {D_OUT[11:0], B1_OUT, A1_OUT}; 2'b11: XMUX <= {D_OUT[11:0], A1_OUT, B1_OUT};
default: XMUX <= 48'hxxxxxxxxxxxx; default: XMUX <= 48'hxxxxxxxxxxxx;
endcase endcase
end end
@ -2125,8 +2125,8 @@ end
// The post-adder. // The post-adder.
wire signed [48:0] X_EXT; wire signed [48:0] X_EXT;
wire signed [48:0] Z_EXT; wire signed [48:0] Z_EXT;
assign X_EXT = XMUX; assign X_EXT = {1'b0, XMUX};
assign Z_EXT = ZMUX; assign Z_EXT = {1'b0, ZMUX};
assign {CARRYOUT_IN, P_IN} = OPMODE_OUT[7] ? (Z_EXT - (X_EXT + CARRYIN_OUT)) : (Z_EXT + X_EXT + CARRYIN_OUT); assign {CARRYOUT_IN, P_IN} = OPMODE_OUT[7] ? (Z_EXT - (X_EXT + CARRYIN_OUT)) : (Z_EXT + X_EXT + CARRYIN_OUT);
// Cascade outputs. // Cascade outputs.

View File

@ -64,7 +64,7 @@ struct SynthXilinxPass : public ScriptPass
log(" (this feature is experimental and incomplete)\n"); log(" (this feature is experimental and incomplete)\n");
log("\n"); log("\n");
log(" -ise\n"); log(" -ise\n");
log(" generate an output netlist suitable for ISE (enables -iopad)\n"); log(" generate an output netlist suitable for ISE\n");
log("\n"); log("\n");
log(" -nobram\n"); log(" -nobram\n");
log(" do not use block RAM cells in output netlist\n"); log(" do not use block RAM cells in output netlist\n");
@ -84,11 +84,9 @@ struct SynthXilinxPass : public ScriptPass
log(" -nodsp\n"); log(" -nodsp\n");
log(" do not use DSP48E1s to implement multipliers and associated logic\n"); log(" do not use DSP48E1s to implement multipliers and associated logic\n");
log("\n"); log("\n");
log(" -iopad\n");
log(" enable I/O buffer insertion (selected automatically by -ise)\n");
log("\n");
log(" -noiopad\n"); log(" -noiopad\n");
log(" disable I/O buffer insertion (only useful with -ise)\n"); log(" disable I/O buffer insertion (useful for hierarchical or \n");
log(" out-of-context flows)\n");
log("\n"); log("\n");
log(" -noclkbuf\n"); log(" -noclkbuf\n");
log(" disable automatic clock buffer insertion\n"); log(" disable automatic clock buffer insertion\n");
@ -125,7 +123,7 @@ struct SynthXilinxPass : public ScriptPass
} }
std::string top_opt, edif_file, blif_file, family; std::string top_opt, edif_file, blif_file, family;
bool flatten, retime, vpr, ise, iopad, noiopad, noclkbuf, nobram, nolutram, nosrl, nocarry, nowidelut, nodsp, uram; bool flatten, retime, vpr, ise, noiopad, noclkbuf, nobram, nolutram, nosrl, nocarry, nowidelut, nodsp, uram;
bool abc9, dff_mode; bool abc9, dff_mode;
bool flatten_before_abc; bool flatten_before_abc;
int widemux; int widemux;
@ -140,7 +138,6 @@ struct SynthXilinxPass : public ScriptPass
retime = false; retime = false;
vpr = false; vpr = false;
ise = false; ise = false;
iopad = false;
noiopad = false; noiopad = false;
noclkbuf = false; noclkbuf = false;
nocarry = false; nocarry = false;
@ -218,7 +215,6 @@ struct SynthXilinxPass : public ScriptPass
continue; continue;
} }
if (args[argidx] == "-iopad") { if (args[argidx] == "-iopad") {
iopad = true;
continue; continue;
} }
if (args[argidx] == "-noiopad") { if (args[argidx] == "-noiopad") {
@ -291,7 +287,6 @@ struct SynthXilinxPass : public ScriptPass
void script() YS_OVERRIDE void script() YS_OVERRIDE
{ {
bool do_iopad = iopad || (ise && !noiopad);
std::string ff_map_file; std::string ff_map_file;
if (help_mode) if (help_mode)
ff_map_file = "+/xilinx/{family}_ff_map.v"; ff_map_file = "+/xilinx/{family}_ff_map.v";
@ -397,7 +392,10 @@ struct SynthXilinxPass : public ScriptPass
run("opt_expr -fine"); run("opt_expr -fine");
run("wreduce"); run("wreduce");
run("select -clear"); run("select -clear");
run("xilinx_dsp"); if (help_mode)
run("xilinx_dsp -family <family>");
else
run("xilinx_dsp -family " + family);
run("chtype -set $mul t:$__soft_mul"); run("chtype -set $mul t:$__soft_mul");
} }
} }
@ -524,8 +522,8 @@ struct SynthXilinxPass : public ScriptPass
if (check_label("map_cells")) { if (check_label("map_cells")) {
// Needs to be done before logic optimization, so that inverters (OE vs T) are handled. // Needs to be done before logic optimization, so that inverters (OE vs T) are handled.
if (help_mode || do_iopad) if (help_mode || !noiopad)
run("iopadmap -bits -outpad OBUF I:O -inpad IBUF O:I -toutpad $__XILINX_TOUTPAD OE:I:O -tinoutpad $__XILINX_TINOUTPAD OE:O:I:IO A:top", "(only if '-iopad' or '-ise' and not '-noiopad')"); run("iopadmap -bits -outpad OBUF I:O -inpad IBUF O:I -toutpad $__XILINX_TOUTPAD OE:I:O -tinoutpad $__XILINX_TINOUTPAD OE:O:I:IO A:top", "(only if not '-noiopad')");
std::string techmap_args = "-map +/techmap.v -map +/xilinx/cells_map.v"; std::string techmap_args = "-map +/techmap.v -map +/xilinx/cells_map.v";
if (widemux > 0) if (widemux > 0)
techmap_args += stringf(" -D MIN_MUX_INPUTS=%d", widemux); techmap_args += stringf(" -D MIN_MUX_INPUTS=%d", widemux);

View File

@ -8,4 +8,8 @@ dsp_work*/
test_dsp_model_ref.v test_dsp_model_ref.v
test_dsp_model_uut.v test_dsp_model_uut.v
test_dsp_model test_dsp_model
test_dsp48a_model_ref.v
test_dsp48a1_model_ref.v
test_dsp48a1_model_uut.v
test_dsp48a1_model
*.vcd *.vcd

View File

@ -0,0 +1,17 @@
#!/bin/bash
set -ex
if [ -z $ISE_DIR ]; then
ISE_DIR=/opt/Xilinx/ISE/14.7
fi
sed 's/DSP48A1/MARKER1/; s/DSP48A/DSP48A_UUT/; s/MARKER1/DSP48A1_UUT/; /module DSP48A_UUT/,/endmodule/ p; /module DSP48A1_UUT/,/endmodule/ p; d;' < ../cells_sim.v > test_dsp48a1_model_uut.v
if [ ! -f "test_dsp48a1_model_ref.v" ]; then
cp $ISE_DIR/ISE_DS/ISE/verilog/src/unisims/DSP48A1.v test_dsp48a1_model_ref.v
fi
if [ ! -f "test_dsp48a_model_ref.v" ]; then
cp $ISE_DIR/ISE_DS/ISE/verilog/src/unisims/DSP48A.v test_dsp48a_model_ref.v
fi
for tb in mult_allreg mult_noreg mult_inreg
do
iverilog -s $tb -s glbl -o test_dsp48a1_model test_dsp48a1_model.v test_dsp48a1_model_uut.v test_dsp48a1_model_ref.v test_dsp48a_model_ref.v $ISE_DIR/ISE_DS/ISE/verilog/src/glbl.v
vvp -N ./test_dsp48a1_model
done

View File

@ -0,0 +1,331 @@
`timescale 1ns / 1ps
module testbench;
parameter integer A0REG = 1;
parameter integer A1REG = 1;
parameter integer B0REG = 1;
parameter integer B1REG = 1;
parameter integer CREG = 1;
parameter integer DREG = 1;
parameter integer MREG = 1;
parameter integer PREG = 1;
parameter integer CARRYINREG = 1;
parameter integer CARRYOUTREG = 1;
parameter integer OPMODEREG = 1;
parameter CARRYINSEL = "OPMODE5";
parameter RSTTYPE = "SYNC";
reg CLK;
reg CEA, CEB, CEC, CED, CEM, CEP, CECARRYIN, CEOPMODE;
reg RSTA, RSTB, RSTC, RSTD, RSTM, RSTP, RSTCARRYIN, RSTOPMODE;
reg [17:0] A;
reg [17:0] B;
reg [47:0] C;
reg [17:0] D;
reg [47:0] PCIN;
reg [7:0] OPMODE;
reg CARRYIN;
output CARRYOUTF, REF_CARRYOUTF;
output CARRYOUT, REF_CARRYOUT, REF_OLD_CARRYOUT;
output [35:0] M, REF_M;
output [47:0] P, REF_P, REF_OLD_P;
output [17:0] BCOUT, REF_BCOUT, REF_OLD_BCOUT;
output [47:0] PCOUT, REF_PCOUT, REF_OLD_PCOUT;
integer errcount = 0;
reg ERROR_FLAG = 0;
task clkcycle;
begin
#5;
CLK = ~CLK;
#10;
CLK = ~CLK;
#2;
ERROR_FLAG = 0;
if (REF_BCOUT !== BCOUT || REF_OLD_BCOUT != BCOUT) begin
$display("ERROR at %1t: REF_BCOUT=%b REF_OLD_BCOUT=%b UUT_BCOUT=%b DIFF=%b", $time, REF_BCOUT, REF_OLD_BCOUT, BCOUT, REF_BCOUT ^ BCOUT);
errcount = errcount + 1;
ERROR_FLAG = 1;
end
if (REF_M !== M) begin
$display("ERROR at %1t: REF_M=%b UUT_M=%b DIFF=%b", $time, REF_M, M, REF_M ^ M);
errcount = errcount + 1;
ERROR_FLAG = 1;
end
if (REF_P !== P || REF_OLD_P != P) begin
$display("ERROR at %1t: REF_P=%b REF_OLD_P=%b UUT_P=%b DIFF=%b", $time, REF_P, REF_OLD_P, P, REF_P ^ P);
errcount = errcount + 1;
ERROR_FLAG = 1;
end
if (REF_PCOUT !== PCOUT || REF_OLD_PCOUT != PCOUT) begin
$display("ERROR at %1t: REF_PCOUT=%b REF_OLD_PCOUT=%b UUT_PCOUT=%b DIFF=%b", $time, REF_PCOUT, REF_OLD_PCOUT, PCOUT, REF_PCOUT ^ PCOUT);
errcount = errcount + 1;
ERROR_FLAG = 1;
end
if (REF_CARRYOUT !== CARRYOUT || (REF_OLD_CARRYOUT != CARRYOUT && !CARRYOUTREG)) begin
$display("ERROR at %1t: REF_CARRYOUT=%b REF_OLD_CARRYOUT=%b UUT_CARRYOUT=%b DIFF=%b", $time, REF_CARRYOUT, REF_OLD_CARRYOUT, CARRYOUT, REF_CARRYOUT ^ CARRYOUT);
errcount = errcount + 1;
ERROR_FLAG = 1;
end
if (REF_CARRYOUTF !== CARRYOUTF) begin
$display("ERROR at %1t: REF_CARRYOUTF=%b UUT_CARRYOUTF=%b", $time, REF_CARRYOUTF, CARRYOUTF);
errcount = errcount + 1;
ERROR_FLAG = 1;
end
#3;
end
endtask
reg config_valid = 0;
task drc;
begin
config_valid = 1;
if (OPMODE[1:0] == 2'b10 && PREG != 1) config_valid = 0;
if (OPMODE[3:2] == 2'b10 && PREG != 1) config_valid = 0;
end
endtask
initial begin
$dumpfile("test_dsp48a1_model.vcd");
$dumpvars(0, testbench);
#2;
CLK = 1'b0;
{CEA, CEB, CEC, CED, CEM, CEP, CECARRYIN, CEOPMODE} = 8'b11111111;
{A, B, C, D, PCIN, OPMODE, CARRYIN} = 0;
{RSTA, RSTB, RSTC, RSTD, RSTM, RSTP, RSTCARRYIN, RSTOPMODE} = 8'b11111111;
repeat (10) begin
#10;
CLK = 1'b1;
#10;
CLK = 1'b0;
#10;
CLK = 1'b1;
#10;
CLK = 1'b0;
end
{RSTA, RSTB, RSTC, RSTD, RSTM, RSTP, RSTCARRYIN, RSTOPMODE} = 0;
repeat (10000) begin
clkcycle;
config_valid = 0;
while (!config_valid) begin
A = $urandom;
B = $urandom;
C = {$urandom, $urandom};
D = $urandom;
PCIN = {$urandom, $urandom};
{CEA, CEB, CEC, CED, CEM, CEP, CECARRYIN, CEOPMODE} = $urandom | $urandom | $urandom;
{RSTA, RSTB, RSTC, RSTD, RSTM, RSTP, RSTCARRYIN, RSTOPMODE} = $urandom & $urandom & $urandom & $urandom & $urandom & $urandom;
{CARRYIN, OPMODE} = $urandom;
drc;
end
end
if (errcount == 0) begin
$display("All tests passed.");
$finish;
end else begin
$display("Caught %1d errors.", errcount);
$stop;
end
end
DSP48A #(
.A0REG (A0REG),
.A1REG (A1REG),
.B0REG (B0REG),
.B1REG (B1REG),
.CREG (CREG),
.DREG (DREG),
.MREG (MREG),
.PREG (PREG),
.CARRYINREG (CARRYINREG),
.OPMODEREG (OPMODEREG),
.CARRYINSEL (CARRYINSEL),
.RSTTYPE (RSTTYPE)
) ref_old (
.A (A),
.B (B),
.C (C),
.D (D),
.PCIN (PCIN),
.CARRYIN (CARRYIN),
.OPMODE (OPMODE),
.BCOUT (REF_OLD_BCOUT),
.CARRYOUT (REF_OLD_CARRYOUT),
.P (REF_OLD_P),
.PCOUT (REF_OLD_PCOUT),
.CEA (CEA),
.CEB (CEB),
.CEC (CEC),
.CED (CED),
.CEM (CEM),
.CEP (CEP),
.CECARRYIN (CECARRYIN),
.CEOPMODE (CEOPMODE),
.CLK (CLK),
.RSTA (RSTA),
.RSTB (RSTB),
.RSTC (RSTC),
.RSTD (RSTD),
.RSTM (RSTM),
.RSTP (RSTP),
.RSTCARRYIN (RSTCARRYIN),
.RSTOPMODE (RSTOPMODE)
);
DSP48A1 #(
.A0REG (A0REG),
.A1REG (A1REG),
.B0REG (B0REG),
.B1REG (B1REG),
.CREG (CREG),
.DREG (DREG),
.MREG (MREG),
.PREG (PREG),
.CARRYINREG (CARRYINREG),
.CARRYOUTREG (CARRYOUTREG),
.OPMODEREG (OPMODEREG),
.CARRYINSEL (CARRYINSEL),
.RSTTYPE (RSTTYPE)
) ref (
.A (A),
.B (B),
.C (C),
.D (D),
.PCIN (PCIN),
.CARRYIN (CARRYIN),
.OPMODE (OPMODE),
.BCOUT (REF_BCOUT),
.CARRYOUTF (REF_CARRYOUTF),
.CARRYOUT (REF_CARRYOUT),
.P (REF_P),
.M (REF_M),
.PCOUT (REF_PCOUT),
.CEA (CEA),
.CEB (CEB),
.CEC (CEC),
.CED (CED),
.CEM (CEM),
.CEP (CEP),
.CECARRYIN (CECARRYIN),
.CEOPMODE (CEOPMODE),
.CLK (CLK),
.RSTA (RSTA),
.RSTB (RSTB),
.RSTC (RSTC),
.RSTD (RSTD),
.RSTM (RSTM),
.RSTP (RSTP),
.RSTCARRYIN (RSTCARRYIN),
.RSTOPMODE (RSTOPMODE)
);
DSP48A1_UUT #(
.A0REG (A0REG),
.A1REG (A1REG),
.B0REG (B0REG),
.B1REG (B1REG),
.CREG (CREG),
.DREG (DREG),
.MREG (MREG),
.PREG (PREG),
.CARRYINREG (CARRYINREG),
.CARRYOUTREG (CARRYOUTREG),
.OPMODEREG (OPMODEREG),
.CARRYINSEL (CARRYINSEL),
.RSTTYPE (RSTTYPE)
) uut (
.A (A),
.B (B),
.C (C),
.D (D),
.PCIN (PCIN),
.CARRYIN (CARRYIN),
.OPMODE (OPMODE),
.BCOUT (BCOUT),
.CARRYOUTF (CARRYOUTF),
.CARRYOUT (CARRYOUT),
.P (P),
.M (M),
.PCOUT (PCOUT),
.CEA (CEA),
.CEB (CEB),
.CEC (CEC),
.CED (CED),
.CEM (CEM),
.CEP (CEP),
.CECARRYIN (CECARRYIN),
.CEOPMODE (CEOPMODE),
.CLK (CLK),
.RSTA (RSTA),
.RSTB (RSTB),
.RSTC (RSTC),
.RSTD (RSTD),
.RSTM (RSTM),
.RSTP (RSTP),
.RSTCARRYIN (RSTCARRYIN),
.RSTOPMODE (RSTOPMODE)
);
endmodule
module mult_noreg;
testbench #(
.A0REG (0),
.A1REG (0),
.B0REG (0),
.B1REG (0),
.CREG (0),
.DREG (0),
.MREG (0),
.PREG (0),
.CARRYINREG (0),
.CARRYOUTREG (0),
.OPMODEREG (0),
.CARRYINSEL ("CARRYIN"),
.RSTTYPE ("SYNC")
) testbench ();
endmodule
module mult_allreg;
testbench #(
.A0REG (1),
.A1REG (1),
.B0REG (1),
.B1REG (1),
.CREG (1),
.DREG (1),
.MREG (1),
.PREG (1),
.CARRYINREG (1),
.CARRYOUTREG (1),
.OPMODEREG (1),
.CARRYINSEL ("OPMODE5"),
.RSTTYPE ("SYNC")
) testbench ();
endmodule
module mult_inreg;
testbench #(
.A0REG (1),
.A1REG (1),
.B0REG (1),
.B1REG (1),
.CREG (1),
.DREG (1),
.MREG (0),
.PREG (0),
.CARRYINREG (1),
.CARRYOUTREG (0),
.OPMODEREG (0),
.CARRYINSEL ("CARRYIN"),
.RSTTYPE ("SYNC")
) testbench ();
endmodule

View File

@ -1,14 +1,17 @@
#!/bin/bash #!/bin/bash
set -ex set -ex
if [ -z $VIVADO_DIR ]; then
VIVADO_DIR=/opt/Xilinx/Vivado/2019.1
fi
sed 's/DSP48E1/DSP48E1_UUT/; /DSP48E1_UUT/,/endmodule/ p; d;' < ../cells_sim.v > test_dsp_model_uut.v sed 's/DSP48E1/DSP48E1_UUT/; /DSP48E1_UUT/,/endmodule/ p; d;' < ../cells_sim.v > test_dsp_model_uut.v
if [ ! -f "test_dsp_model_ref.v" ]; then if [ ! -f "test_dsp_model_ref.v" ]; then
cat /opt/Xilinx/Vivado/2019.1/data/verilog/src/unisims/DSP48E1.v > test_dsp_model_ref.v cp $VIVADO_DIR/data/verilog/src/unisims/DSP48E1.v test_dsp_model_ref.v
fi fi
for tb in macc_overflow_underflow \ for tb in macc_overflow_underflow \
simd24_preadd_noreg_nocasc simd12_preadd_noreg_nocasc \ simd24_preadd_noreg_nocasc simd12_preadd_noreg_nocasc \
mult_allreg_nopreadd_nocasc mult_noreg_nopreadd_nocasc \ mult_allreg_nopreadd_nocasc mult_noreg_nopreadd_nocasc \
mult_allreg_preadd_nocasc mult_noreg_preadd_nocasc mult_inreg_preadd_nocasc mult_allreg_preadd_nocasc mult_noreg_preadd_nocasc mult_inreg_preadd_nocasc
do do
iverilog -s $tb -s glbl -o test_dsp_model test_dsp_model.v test_dsp_model_uut.v test_dsp_model_ref.v /opt/Xilinx/Vivado/2019.1/data/verilog/src/glbl.v iverilog -s $tb -s glbl -o test_dsp_model test_dsp_model.v test_dsp_model_uut.v test_dsp_model_ref.v $VIVADO_DIR/data/verilog/src/glbl.v
vvp -N ./test_dsp_model vvp -N ./test_dsp_model
done done

View File

@ -27,7 +27,7 @@ module \$__MUL18X18 (input [17:0] A, input [17:0] B, output [35:0] Y);
.D(18'b0), .D(18'b0),
.P(P_48), .P(P_48),
.OPMODE(8'b0000010) .OPMODE(8'b0000001)
); );
assign Y = P_48; assign Y = P_48;
endmodule endmodule

View File

@ -27,7 +27,7 @@ module \$__MUL18X18 (input [17:0] A, input [17:0] B, output [35:0] Y);
.D(18'b0), .D(18'b0),
.P(P_48), .P(P_48),
.OPMODE(8'b0000010) .OPMODE(8'b0000001)
); );
assign Y = P_48; assign Y = P_48;
endmodule endmodule

View File

@ -0,0 +1,16 @@
read_verilog <<EOT
module led_blink (
input clk,
output ledc
);
reg [6:0] led_counter = 0;
always @( posedge clk ) begin
led_counter <= led_counter + 1;
end
assign ledc = !led_counter[ 6:3 ];
endmodule
EOT
proc
equiv_opt -assert -map +/ecp5/cells_sim.v synth_ecp5 -abc9

View File

@ -39,8 +39,8 @@ proc
equiv_opt -assert -map +/ecp5/cells_sim.v synth_ecp5 # equivalency check equiv_opt -assert -map +/ecp5/cells_sim.v synth_ecp5 # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd mux16 # Constrain all select calls below inside the top module cd mux16 # Constrain all select calls below inside the top module
select -assert-count 8 t:L6MUX21 select -assert-count 12 t:L6MUX21
select -assert-count 26 t:LUT4 select -assert-count 34 t:LUT4
select -assert-count 12 t:PFUMX select -assert-count 17 t:PFUMX
select -assert-none t:LUT4 t:L6MUX21 t:PFUMX %% t:* %D select -assert-none t:LUT4 t:L6MUX21 t:PFUMX %% t:* %D

View File

@ -0,0 +1,16 @@
read_verilog <<EOT
module led_blink (
input clk,
output ledc
);
reg [6:0] led_counter = 0;
always @( posedge clk ) begin
led_counter <= led_counter + 1;
end
assign ledc = !led_counter[ 6:3 ];
endmodule
EOT
proc
equiv_opt -assert -map +/ice40/cells_sim.v synth_ice40 -abc9

View File

@ -1,7 +1,7 @@
read_verilog ../common/add_sub.v read_verilog ../common/add_sub.v
hierarchy -top top hierarchy -top top
proc proc
equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx # equivalency check equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd top # Constrain all select calls below inside the top module cd top # Constrain all select calls below inside the top module
select -assert-count 14 t:LUT2 select -assert-count 14 t:LUT2

View File

@ -3,7 +3,7 @@ design -save read
hierarchy -top adff hierarchy -top adff
proc proc
equiv_opt -async2sync -assert -map +/xilinx/cells_sim.v synth_xilinx # equivalency check equiv_opt -async2sync -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd adff # Constrain all select calls below inside the top module cd adff # Constrain all select calls below inside the top module
select -assert-count 1 t:BUFG select -assert-count 1 t:BUFG
@ -15,7 +15,7 @@ select -assert-none t:BUFG t:FDCE %% t:* %D
design -load read design -load read
hierarchy -top adffn hierarchy -top adffn
proc proc
equiv_opt -async2sync -assert -map +/xilinx/cells_sim.v synth_xilinx # equivalency check equiv_opt -async2sync -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd adffn # Constrain all select calls below inside the top module cd adffn # Constrain all select calls below inside the top module
select -assert-count 1 t:BUFG select -assert-count 1 t:BUFG
@ -28,7 +28,7 @@ select -assert-none t:BUFG t:FDCE t:INV %% t:* %D
design -load read design -load read
hierarchy -top dffs hierarchy -top dffs
proc proc
equiv_opt -async2sync -assert -map +/xilinx/cells_sim.v synth_xilinx # equivalency check equiv_opt -async2sync -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd dffs # Constrain all select calls below inside the top module cd dffs # Constrain all select calls below inside the top module
select -assert-count 1 t:BUFG select -assert-count 1 t:BUFG
@ -40,7 +40,7 @@ select -assert-none t:BUFG t:FDSE %% t:* %D
design -load read design -load read
hierarchy -top ndffnr hierarchy -top ndffnr
proc proc
equiv_opt -async2sync -assert -map +/xilinx/cells_sim.v synth_xilinx # equivalency check equiv_opt -async2sync -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd ndffnr # Constrain all select calls below inside the top module cd ndffnr # Constrain all select calls below inside the top module
select -assert-count 1 t:BUFG select -assert-count 1 t:BUFG

View File

@ -1,7 +1,7 @@
# Check that blockram memory without parameters is not modified # Check that blockram memory without parameters is not modified
read_verilog ../common/memory_attributes/attributes_test.v read_verilog ../common/memory_attributes/attributes_test.v
hierarchy -top block_ram hierarchy -top block_ram
synth_xilinx -top block_ram synth_xilinx -top block_ram -noiopad
cd block_ram # Constrain all select calls below inside the top module cd block_ram # Constrain all select calls below inside the top module
select -assert-count 1 t:RAMB18E1 select -assert-count 1 t:RAMB18E1
@ -9,7 +9,7 @@ select -assert-count 1 t:RAMB18E1
design -reset design -reset
read_verilog ../common/memory_attributes/attributes_test.v read_verilog ../common/memory_attributes/attributes_test.v
hierarchy -top distributed_ram hierarchy -top distributed_ram
synth_xilinx -top distributed_ram synth_xilinx -top distributed_ram -noiopad
cd distributed_ram # Constrain all select calls below inside the top module cd distributed_ram # Constrain all select calls below inside the top module
select -assert-count 8 t:RAM32X1D select -assert-count 8 t:RAM32X1D
@ -18,7 +18,7 @@ design -reset
read_verilog ../common/memory_attributes/attributes_test.v read_verilog ../common/memory_attributes/attributes_test.v
prep prep
setattr -mod -set ram_style "distributed" block_ram setattr -mod -set ram_style "distributed" block_ram
synth_xilinx -top block_ram synth_xilinx -top block_ram -noiopad
cd block_ram # Constrain all select calls below inside the top module cd block_ram # Constrain all select calls below inside the top module
select -assert-count 32 t:RAM128X1D select -assert-count 32 t:RAM128X1D
@ -27,7 +27,7 @@ design -reset
read_verilog ../common/memory_attributes/attributes_test.v read_verilog ../common/memory_attributes/attributes_test.v
prep prep
setattr -mod -set logic_block 1 block_ram setattr -mod -set logic_block 1 block_ram
synth_xilinx -top block_ram synth_xilinx -top block_ram -noiopad
cd block_ram # Constrain all select calls below inside the top module cd block_ram # Constrain all select calls below inside the top module
select -assert-count 0 t:RAMB18E1 select -assert-count 0 t:RAMB18E1
select -assert-count 32 t:RAM128X1D select -assert-count 32 t:RAM128X1D
@ -35,13 +35,13 @@ select -assert-count 32 t:RAM128X1D
# Set ram_style block to a distributed memory; will be implemented as blockram # Set ram_style block to a distributed memory; will be implemented as blockram
design -reset design -reset
read_verilog ../common/memory_attributes/attributes_test.v read_verilog ../common/memory_attributes/attributes_test.v
synth_xilinx -top distributed_ram_manual synth_xilinx -top distributed_ram_manual -noiopad
cd distributed_ram_manual # Constrain all select calls below inside the top module cd distributed_ram_manual # Constrain all select calls below inside the top module
select -assert-count 1 t:RAMB18E1 select -assert-count 1 t:RAMB18E1
# Set synthesis, ram_block block to a distributed memory; will be implemented as blockram # Set synthesis, ram_block block to a distributed memory; will be implemented as blockram
design -reset design -reset
read_verilog ../common/memory_attributes/attributes_test.v read_verilog ../common/memory_attributes/attributes_test.v
synth_xilinx -top distributed_ram_manual_syn synth_xilinx -top distributed_ram_manual_syn -noiopad
cd distributed_ram_manual_syn # Constrain all select calls below inside the top module cd distributed_ram_manual_syn # Constrain all select calls below inside the top module
select -assert-count 1 t:RAMB18E1 select -assert-count 1 t:RAMB18E1

View File

@ -3,28 +3,28 @@
# Memory bits <= 18K; Data width <= 36; Address width <= 14: -> RAMB18E1 # Memory bits <= 18K; Data width <= 36; Address width <= 14: -> RAMB18E1
read_verilog ../common/blockram.v read_verilog ../common/blockram.v
chparam -set ADDRESS_WIDTH 10 -set DATA_WIDTH 1 sync_ram_sdp chparam -set ADDRESS_WIDTH 10 -set DATA_WIDTH 1 sync_ram_sdp
synth_xilinx -top sync_ram_sdp synth_xilinx -top sync_ram_sdp -noiopad
cd sync_ram_sdp cd sync_ram_sdp
select -assert-count 1 t:RAMB18E1 select -assert-count 1 t:RAMB18E1
design -reset design -reset
read_verilog ../common/blockram.v read_verilog ../common/blockram.v
chparam -set ADDRESS_WIDTH 8 -set DATA_WIDTH 18 sync_ram_sdp chparam -set ADDRESS_WIDTH 8 -set DATA_WIDTH 18 sync_ram_sdp
synth_xilinx -top sync_ram_sdp synth_xilinx -top sync_ram_sdp -noiopad
cd sync_ram_sdp cd sync_ram_sdp
select -assert-count 1 t:RAMB18E1 select -assert-count 1 t:RAMB18E1
design -reset design -reset
read_verilog ../common/blockram.v read_verilog ../common/blockram.v
chparam -set ADDRESS_WIDTH 14 -set DATA_WIDTH 1 sync_ram_sdp chparam -set ADDRESS_WIDTH 14 -set DATA_WIDTH 1 sync_ram_sdp
synth_xilinx -top sync_ram_sdp synth_xilinx -top sync_ram_sdp -noiopad
cd sync_ram_sdp cd sync_ram_sdp
select -assert-count 1 t:RAMB18E1 select -assert-count 1 t:RAMB18E1
design -reset design -reset
read_verilog ../common/blockram.v read_verilog ../common/blockram.v
chparam -set ADDRESS_WIDTH 9 -set DATA_WIDTH 36 sync_ram_sdp chparam -set ADDRESS_WIDTH 9 -set DATA_WIDTH 36 sync_ram_sdp
synth_xilinx -top sync_ram_sdp synth_xilinx -top sync_ram_sdp -noiopad
cd sync_ram_sdp cd sync_ram_sdp
select -assert-count 1 t:RAMB18E1 select -assert-count 1 t:RAMB18E1
@ -32,7 +32,7 @@ select -assert-count 1 t:RAMB18E1
design -reset design -reset
read_verilog ../common/blockram.v read_verilog ../common/blockram.v
chparam -set ADDRESS_WIDTH 8 -set DATA_WIDTH 2 sync_ram_sdp chparam -set ADDRESS_WIDTH 8 -set DATA_WIDTH 2 sync_ram_sdp
synth_xilinx -top sync_ram_sdp synth_xilinx -top sync_ram_sdp -noiopad
cd sync_ram_sdp cd sync_ram_sdp
select -assert-count 0 t:RAMB18E1 select -assert-count 0 t:RAMB18E1
select -assert-count 4 t:RAM128X1D select -assert-count 4 t:RAM128X1D
@ -41,7 +41,7 @@ select -assert-count 4 t:RAM128X1D
design -reset design -reset
read_verilog ../common/blockram.v read_verilog ../common/blockram.v
chparam -set ADDRESS_WIDTH 10 -set DATA_WIDTH 36 sync_ram_sdp chparam -set ADDRESS_WIDTH 10 -set DATA_WIDTH 36 sync_ram_sdp
synth_xilinx -top sync_ram_sdp synth_xilinx -top sync_ram_sdp -noiopad
cd sync_ram_sdp cd sync_ram_sdp
select -assert-count 1 t:RAMB36E1 select -assert-count 1 t:RAMB36E1
@ -52,7 +52,7 @@ design -reset
read_verilog ../common/blockram.v read_verilog ../common/blockram.v
hierarchy -top sync_ram_sdp -chparam ADDRESS_WIDTH 10 -chparam DATA_WIDTH 1 hierarchy -top sync_ram_sdp -chparam ADDRESS_WIDTH 10 -chparam DATA_WIDTH 1
setattr -set ram_style "block" m:memory setattr -set ram_style "block" m:memory
synth_xilinx -top sync_ram_sdp synth_xilinx -top sync_ram_sdp -noiopad
cd sync_ram_sdp cd sync_ram_sdp
select -assert-count 1 t:RAMB18E1 select -assert-count 1 t:RAMB18E1
@ -60,7 +60,7 @@ design -reset
read_verilog ../common/blockram.v read_verilog ../common/blockram.v
hierarchy -top sync_ram_sdp -chparam ADDRESS_WIDTH 10 -chparam DATA_WIDTH 1 hierarchy -top sync_ram_sdp -chparam ADDRESS_WIDTH 10 -chparam DATA_WIDTH 1
setattr -set ram_block 1 m:memory setattr -set ram_block 1 m:memory
synth_xilinx -top sync_ram_sdp synth_xilinx -top sync_ram_sdp -noiopad
cd sync_ram_sdp cd sync_ram_sdp
select -assert-count 1 t:RAMB18E1 select -assert-count 1 t:RAMB18E1
@ -68,7 +68,7 @@ design -reset
read_verilog ../common/blockram.v read_verilog ../common/blockram.v
hierarchy -top sync_ram_sdp -chparam ADDRESS_WIDTH 10 -chparam DATA_WIDTH 1 hierarchy -top sync_ram_sdp -chparam ADDRESS_WIDTH 10 -chparam DATA_WIDTH 1
setattr -set ram_style "dont_infer_a_ram_pretty_please" m:memory setattr -set ram_style "dont_infer_a_ram_pretty_please" m:memory
synth_xilinx -top sync_ram_sdp synth_xilinx -top sync_ram_sdp -noiopad
cd sync_ram_sdp cd sync_ram_sdp
select -assert-count 0 t:RAMB18E1 select -assert-count 0 t:RAMB18E1
@ -76,7 +76,7 @@ design -reset
read_verilog ../common/blockram.v read_verilog ../common/blockram.v
hierarchy -top sync_ram_sdp -chparam ADDRESS_WIDTH 10 -chparam DATA_WIDTH 1 hierarchy -top sync_ram_sdp -chparam ADDRESS_WIDTH 10 -chparam DATA_WIDTH 1
setattr -set logic_block 1 m:memory setattr -set logic_block 1 m:memory
synth_xilinx -top sync_ram_sdp synth_xilinx -top sync_ram_sdp -noiopad
cd sync_ram_sdp cd sync_ram_sdp
select -assert-count 0 t:RAMB18E1 select -assert-count 0 t:RAMB18E1
@ -84,7 +84,7 @@ design -reset
read_verilog ../common/blockram.v read_verilog ../common/blockram.v
hierarchy -top sync_ram_sdp -chparam ADDRESS_WIDTH 8 -chparam DATA_WIDTH 1 hierarchy -top sync_ram_sdp -chparam ADDRESS_WIDTH 8 -chparam DATA_WIDTH 1
setattr -set ram_style "block" m:memory setattr -set ram_style "block" m:memory
synth_xilinx -top sync_ram_sdp synth_xilinx -top sync_ram_sdp -noiopad
cd sync_ram_sdp cd sync_ram_sdp
select -assert-count 1 t:RAMB18E1 select -assert-count 1 t:RAMB18E1
@ -92,6 +92,6 @@ design -reset
read_verilog ../common/blockram.v read_verilog ../common/blockram.v
hierarchy -top sync_ram_sdp -chparam ADDRESS_WIDTH 8 -chparam DATA_WIDTH 1 hierarchy -top sync_ram_sdp -chparam ADDRESS_WIDTH 8 -chparam DATA_WIDTH 1
setattr -set ram_block 1 m:memory setattr -set ram_block 1 m:memory
synth_xilinx -top sync_ram_sdp synth_xilinx -top sync_ram_sdp -noiopad
cd sync_ram_sdp cd sync_ram_sdp
select -assert-count 1 t:RAMB18E1 select -assert-count 1 t:RAMB18E1

View File

@ -28,7 +28,7 @@ module register_file(
endmodule endmodule
EOT EOT
synth_xilinx synth_xilinx -noiopad
cd register_file cd register_file
select -assert-count 32 t:RAM32M select -assert-count 32 t:RAM32M
select -assert-none t:* t:BUFG %d t:RAM32M %d select -assert-none t:* t:BUFG %d t:RAM32M %d

View File

@ -0,0 +1,16 @@
read_verilog <<EOT
module led_blink (
input clk,
output ledc
);
reg [6:0] led_counter = 0;
always @( posedge clk ) begin
led_counter <= led_counter + 1;
end
assign ledc = !led_counter[ 6:3 ];
endmodule
EOT
proc
equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -abc9

View File

@ -2,7 +2,7 @@ read_verilog ../common/counter.v
hierarchy -top top hierarchy -top top
proc proc
flatten flatten
equiv_opt -async2sync -assert -map +/xilinx/cells_sim.v synth_xilinx # equivalency check equiv_opt -async2sync -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd top # Constrain all select calls below inside the top module cd top # Constrain all select calls below inside the top module

View File

@ -3,7 +3,7 @@ design -save read
hierarchy -top dff hierarchy -top dff
proc proc
equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx # equivalency check equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd dff # Constrain all select calls below inside the top module cd dff # Constrain all select calls below inside the top module
select -assert-count 1 t:BUFG select -assert-count 1 t:BUFG
@ -15,7 +15,7 @@ select -assert-none t:BUFG t:FDRE %% t:* %D
design -load read design -load read
hierarchy -top dffe hierarchy -top dffe
proc proc
equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx # equivalency check equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd dffe # Constrain all select calls below inside the top module cd dffe # Constrain all select calls below inside the top module
select -assert-count 1 t:BUFG select -assert-count 1 t:BUFG

View File

@ -0,0 +1,89 @@
design -reset
read_verilog <<EOT
module cascade(input clk, input [4:0] a, input [4:0] b, output reg [9:0] o);
reg [4:0] ar1, ar2, ar3, br1, br2, br3;
reg [9:0] m, n;
always @(posedge clk) begin
ar1 <= a;
ar2 <= ar1;
ar3 <= ar2;
br1 <= b;
br2 <= br1;
br3 <= br2;
m <= ar1 * br1;
n <= ar2 * br2 + m;
o <= ar3 * br3 + n;
end
endmodule
EOT
proc
design -save read
equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad
design -load postopt
cd cascade
select -assert-count 3 t:DSP48E1
select -assert-none t:DSP48E1 t:BUFG %% t:* %D
# Very crude method of checking that DSP48E1.PCOUT -> DSP48E1.PCIN
# (i.e. Take all DSP48E1s, expand to find all wires connected
# to its PCOUT port, then remove all DSP48E1s from this
# selection, then expand again to find all cells where
# those wires are connected to the PCIN port, then remove
# all wires from this selection, and lastly intersect
# this selection with all DSP48E1 cells (to check that
# the connected cells are indeed DSPs)
select -assert-count 2 t:DSP48E1 %co:+[PCOUT] t:DSP48E1 %d %co:+[PCIN] w:* %d t:DSP48E1 %i
design -load read
equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -family xc6s -noiopad
design -load postopt
cd cascade
select -assert-count 3 t:DSP48A1
select -assert-count 5 t:FDRE # No cascade for A input
select -assert-none t:DSP48A1 t:BUFG t:FDRE %% t:* %D
# Very crude method of checking that DSP48E1.PCOUT -> DSP48E1.PCIN
# (see above for explanation)
select -assert-count 2 t:DSP48A1 %co:+[PCOUT] t:DSP48A1 %d %co:+[PCIN] w:* %d t:DSP48A1 %i
design -reset
read_verilog <<EOT
module cascade(input clk, input [4:0] a, input [4:0] b, output reg [9:0] o);
reg [4:0] ar1, ar2, ar3, br1, br2, br3;
reg [9:0] m;
always @(posedge clk) begin
ar1 <= a;
ar2 <= ar1;
ar3 <= ar2;
br1 <= b;
br2 <= br1;
br3 <= br2;
m <= ar2 * br2;
o <= ar3 * br3 + m;
end
endmodule
EOT
proc
design -save read
equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad
design -load postopt
cd cascade
select -assert-count 2 t:DSP48E1
select -assert-none t:DSP48E1 t:BUFG %% t:* %D
# Very crude method of checking that DSP48E1.PCOUT -> DSP48E1.PCIN
# (see above for explanation)
select -assert-count 1 t:DSP48E1 %co:+[PCOUT] t:DSP48E1 %d %co:+[PCIN] w:* %d t:DSP48E1 %i
design -load read
equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -family xc6s -noiopad
design -load postopt
cd cascade
select -assert-count 2 t:DSP48A1
select -assert-count 10 t:FDRE # Cannot cascade because first 'm' DSP
# uses both B0REG and B1REG, whereas 'o'
# only requires 1
select -assert-none t:DSP48A1 t:BUFG t:FDRE %% t:* %D
# Very crude method of checking that DSP48E1.PCOUT -> DSP48E1.PCIN
# (see above for explanation)
select -assert-count 1 t:DSP48A1 %co:+[PCOUT] t:DSP48A1 %d %co:+[PCIN] w:* %d t:DSP48A1 %i

View File

@ -63,7 +63,7 @@ module fastfir_dynamictaps(i_clk, i_reset, i_tap_wr, i_tap, i_ce, i_sample, o_re
endmodule endmodule
EOT EOT
synth_xilinx synth_xilinx -noiopad
cd fastfir_dynamictaps cd fastfir_dynamictaps
select -assert-count 2 t:DSP48E1 select -assert-count 2 t:DSP48E1
select -assert-none t:* t:DSP48E1 %d t:BUFG %d select -assert-none t:* t:DSP48E1 %d t:BUFG %d

View File

@ -3,7 +3,7 @@ hierarchy -top fsm
proc proc
flatten flatten
equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx -noiopad
miter -equiv -make_assert -flatten gold gate miter miter -equiv -make_assert -flatten gold gate miter
sat -verify -prove-asserts -show-public -set-at 1 in_reset 1 -seq 20 -prove-skip 1 miter sat -verify -prove-asserts -show-public -set-at 1 in_reset 1 -seq 20 -prove-skip 1 miter

View File

@ -3,7 +3,7 @@ design -save read
hierarchy -top latchp hierarchy -top latchp
proc proc
equiv_opt -async2sync -assert -map +/xilinx/cells_sim.v synth_xilinx # equivalency check equiv_opt -async2sync -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd latchp # Constrain all select calls below inside the top module cd latchp # Constrain all select calls below inside the top module
select -assert-count 1 t:LDCE select -assert-count 1 t:LDCE
@ -14,7 +14,7 @@ select -assert-none t:LDCE %% t:* %D
design -load read design -load read
hierarchy -top latchn hierarchy -top latchn
proc proc
equiv_opt -async2sync -assert -map +/xilinx/cells_sim.v synth_xilinx # equivalency check equiv_opt -async2sync -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd latchn # Constrain all select calls below inside the top module cd latchn # Constrain all select calls below inside the top module
select -assert-count 1 t:LDCE select -assert-count 1 t:LDCE
@ -26,7 +26,7 @@ select -assert-none t:LDCE t:INV %% t:* %D
design -load read design -load read
hierarchy -top latchsr hierarchy -top latchsr
proc proc
equiv_opt -async2sync -assert -map +/xilinx/cells_sim.v synth_xilinx # equivalency check equiv_opt -async2sync -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd latchsr # Constrain all select calls below inside the top module cd latchsr # Constrain all select calls below inside the top module
select -assert-count 1 t:LDCE select -assert-count 1 t:LDCE

View File

@ -1,7 +1,7 @@
read_verilog ../common/logic.v read_verilog ../common/logic.v
hierarchy -top top hierarchy -top top
proc proc
equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx # equivalency check equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd top # Constrain all select calls below inside the top module cd top # Constrain all select calls below inside the top module

View File

@ -2,7 +2,7 @@
#hierarchy -top lutram_1w1r -chparam A_WIDTH 4 #hierarchy -top lutram_1w1r -chparam A_WIDTH 4
#proc #proc
#memory -nomap #memory -nomap
#equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx #equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx -noiopad
#memory #memory
#opt -full #opt -full
# #
@ -22,7 +22,7 @@ read_verilog ../common/lutram.v
hierarchy -top lutram_1w1r -chparam A_WIDTH 5 hierarchy -top lutram_1w1r -chparam A_WIDTH 5
proc proc
memory -nomap memory -nomap
equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx -noiopad
memory memory
opt -full opt -full
@ -42,7 +42,7 @@ read_verilog ../common/lutram.v
hierarchy -top lutram_1w1r hierarchy -top lutram_1w1r
proc proc
memory -nomap memory -nomap
equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx -noiopad
memory memory
opt -full opt -full
@ -62,7 +62,7 @@ read_verilog ../common/lutram.v
hierarchy -top lutram_1w3r hierarchy -top lutram_1w3r
proc proc
memory -nomap memory -nomap
equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx -noiopad
memory memory
opt -full opt -full
@ -82,7 +82,7 @@ read_verilog ../common/lutram.v
hierarchy -top lutram_1w3r -chparam A_WIDTH 6 hierarchy -top lutram_1w3r -chparam A_WIDTH 6
proc proc
memory -nomap memory -nomap
equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx -noiopad
memory memory
opt -full opt -full
@ -102,7 +102,7 @@ read_verilog ../common/lutram.v
hierarchy -top lutram_1w1r -chparam A_WIDTH 5 -chparam D_WIDTH 6 hierarchy -top lutram_1w1r -chparam A_WIDTH 5 -chparam D_WIDTH 6
proc proc
memory -nomap memory -nomap
equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx -noiopad
memory memory
opt -full opt -full
@ -122,7 +122,7 @@ read_verilog ../common/lutram.v
hierarchy -top lutram_1w1r -chparam A_WIDTH 6 -chparam D_WIDTH 6 hierarchy -top lutram_1w1r -chparam A_WIDTH 6 -chparam D_WIDTH 6
proc proc
memory -nomap memory -nomap
equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx -noiopad
memory memory
opt -full opt -full

View File

@ -1,3 +1,6 @@
../../../yosys -qp "synth_xilinx -top macc2; rename -top macc2_uut" -o macc_uut.v macc.v ../../../yosys -qp "synth_xilinx -top macc2; rename -top macc2_uut" -o macc_uut.v macc.v
iverilog -o test_macc macc_tb.v macc_uut.v macc.v ../../../techlibs/xilinx/cells_sim.v iverilog -o test_macc macc_tb.v macc_uut.v macc.v ../../../techlibs/xilinx/cells_sim.v
vvp -N ./test_macc vvp -N ./test_macc
../../../yosys -qp "synth_xilinx -family xc6s -top macc2; rename -top macc2_uut" -o macc_uut.v macc.v
iverilog -o test_macc macc_tb.v macc_uut.v macc.v ../../../techlibs/xilinx/cells_sim.v
vvp -N ./test_macc

View File

@ -3,8 +3,8 @@ design -save read
hierarchy -top macc hierarchy -top macc
proc proc
#equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx ### TODO #equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad ### TODO
equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx -noiopad
miter -equiv -flatten -make_assert -make_outputs gold gate miter miter -equiv -flatten -make_assert -make_outputs gold gate miter
sat -verify -prove-asserts -seq 10 -show-inputs -show-outputs miter sat -verify -prove-asserts -seq 10 -show-inputs -show-outputs miter
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
@ -17,8 +17,8 @@ select -assert-none t:BUFG t:FDRE t:DSP48E1 %% t:* %D
design -load read design -load read
hierarchy -top macc2 hierarchy -top macc2
proc proc
#equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx ### TODO #equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad ### TODO
equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx -noiopad
miter -equiv -flatten -make_assert -make_outputs gold gate miter miter -equiv -flatten -make_assert -make_outputs gold gate miter
sat -verify -prove-asserts -seq 10 -show-inputs -show-outputs miter sat -verify -prove-asserts -seq 10 -show-inputs -show-outputs miter
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)

View File

@ -1,9 +1,21 @@
read_verilog ../common/mul.v read_verilog ../common/mul.v
hierarchy -top top hierarchy -top top
proc proc
equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx # equivalency check equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd top # Constrain all select calls below inside the top module cd top # Constrain all select calls below inside the top module
select -assert-count 1 t:DSP48E1 select -assert-count 1 t:DSP48E1
select -assert-none t:DSP48E1 %% t:* %D select -assert-none t:DSP48E1 %% t:* %D
design -reset
read_verilog ../common/mul.v
hierarchy -top top
proc
equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -family xc6s -noiopad # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd top # Constrain all select calls below inside the top module
select -assert-count 1 t:DSP48A1
select -assert-none t:DSP48A1 %% t:* %D

View File

@ -2,10 +2,24 @@ read_verilog mul_unsigned.v
hierarchy -top mul_unsigned hierarchy -top mul_unsigned
proc proc
equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx # equivalency check equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd mul_unsigned # Constrain all select calls below inside the top module cd mul_unsigned # Constrain all select calls below inside the top module
select -assert-count 1 t:BUFG select -assert-count 1 t:BUFG
select -assert-count 1 t:DSP48E1 select -assert-count 1 t:DSP48E1
select -assert-count 30 t:FDRE select -assert-count 30 t:FDRE
select -assert-none t:DSP48E1 t:FDRE t:BUFG %% t:* %D select -assert-none t:DSP48E1 t:FDRE t:BUFG %% t:* %D
design -reset
read_verilog mul_unsigned.v
hierarchy -top mul_unsigned
proc
equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -family xc6s -noiopad # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd mul_unsigned # Constrain all select calls below inside the top module
select -assert-count 1 t:BUFG
select -assert-count 1 t:DSP48A1
select -assert-count 30 t:FDRE
select -assert-none t:DSP48A1 t:FDRE t:BUFG %% t:* %D

View File

@ -3,7 +3,7 @@ design -save read
hierarchy -top mux2 hierarchy -top mux2
proc proc
equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx # equivalency check equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd mux2 # Constrain all select calls below inside the top module cd mux2 # Constrain all select calls below inside the top module
select -assert-count 1 t:LUT3 select -assert-count 1 t:LUT3
@ -14,7 +14,7 @@ select -assert-none t:LUT3 %% t:* %D
design -load read design -load read
hierarchy -top mux4 hierarchy -top mux4
proc proc
equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx # equivalency check equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd mux4 # Constrain all select calls below inside the top module cd mux4 # Constrain all select calls below inside the top module
select -assert-count 1 t:LUT6 select -assert-count 1 t:LUT6
@ -25,7 +25,7 @@ select -assert-none t:LUT6 %% t:* %D
design -load read design -load read
hierarchy -top mux8 hierarchy -top mux8
proc proc
equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx # equivalency check equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd mux8 # Constrain all select calls below inside the top module cd mux8 # Constrain all select calls below inside the top module
select -assert-count 1 t:LUT3 select -assert-count 1 t:LUT3
@ -37,7 +37,7 @@ select -assert-none t:LUT3 t:LUT6 %% t:* %D
design -load read design -load read
hierarchy -top mux16 hierarchy -top mux16
proc proc
equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx # equivalency check equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd mux16 # Constrain all select calls below inside the top module cd mux16 # Constrain all select calls below inside the top module
select -assert-min 5 t:LUT6 select -assert-min 5 t:LUT6

View File

@ -2,7 +2,7 @@ read_verilog ../common/shifter.v
hierarchy -top top hierarchy -top top
proc proc
flatten flatten
equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx # equivalency check equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd top # Constrain all select calls below inside the top module cd top # Constrain all select calls below inside the top module

View File

@ -7,6 +7,7 @@ synth
equiv_opt -assert -map +/xilinx/cells_sim.v -map +/simcells.v synth_xilinx # equivalency check equiv_opt -assert -map +/xilinx/cells_sim.v -map +/simcells.v synth_xilinx # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd tristate # Constrain all select calls below inside the top module cd tristate # Constrain all select calls below inside the top module
# TODO :: Tristate logic not yet supported; see https://github.com/YosysHQ/yosys/issues/1225 select -assert-count 2 t:IBUF
select -assert-count 1 t:$_TBUF_ select -assert-count 1 t:INV
select -assert-none t:$_TBUF_ %% t:* %D select -assert-count 1 t:OBUFT
select -assert-none t:IBUF t:INV t:OBUFT %% t:* %D

View File

@ -28,6 +28,20 @@ assign io = oe ? i : 1'bz;
assign o2 = io; assign o2 = io;
assign o3 = ~io; assign o3 = ~io;
endmodule endmodule
module f(output o, o2);
assign o = 1'bz;
endmodule
module g(inout io, output o);
assign o = io;
endmodule
module h(inout io, output o, input i);
assign io = i;
assign o = io;
endmodule
EOT EOT
opt_clean opt_clean
@ -97,3 +111,12 @@ select -assert-count 1 @oeb %co %co @iob %i
select -assert-count 1 @iob %co %co @o2b %i select -assert-count 1 @iob %co %co @o2b %i
select -assert-count 1 @iob %co %co t:$_NOT_ %i select -assert-count 1 @iob %co %co t:$_NOT_ %i
select -assert-count 1 @o3b %ci %ci t:$_NOT_ %i select -assert-count 1 @o3b %ci %ci t:$_NOT_ %i
select -assert-count 2 f/t:obuft
select -assert-count 1 g/t:obuf
select -assert-count 1 g/t:iobuf
select -assert-count 1 h/t:ibuf
select -assert-count 1 h/t:iobuf
select -assert-count 1 h/t:obuf