diff options
48 files changed, 3111 insertions, 412 deletions
@@ -88,7 +88,7 @@ ifeq ($(OS), Darwin) PLUGIN_LDFLAGS += -undefined dynamic_lookup # homebrew search paths -ifneq ($(shell which brew),) +ifneq ($(shell :; command -v brew),) BREW_PREFIX := $(shell brew --prefix)/opt $(info $$BREW_PREFIX is [${BREW_PREFIX}]) ifeq ($(ENABLE_PYOSYS),1) @@ -102,8 +102,8 @@ PKG_CONFIG_PATH := $(BREW_PREFIX)/tcl-tk/lib/pkgconfig:$(PKG_CONFIG_PATH) export PATH := $(BREW_PREFIX)/bison/bin:$(BREW_PREFIX)/gettext/bin:$(BREW_PREFIX)/flex/bin:$(PATH) # macports search paths -else ifneq ($(shell which port),) -PORT_PREFIX := $(patsubst %/bin/port,%,$(shell which port)) +else ifneq ($(shell :; command -v port),) +PORT_PREFIX := $(patsubst %/bin/port,%,$(shell :; command -v port)) CXXFLAGS += -I$(PORT_PREFIX)/include LDFLAGS += -L$(PORT_PREFIX)/lib PKG_CONFIG_PATH := $(PORT_PREFIX)/lib/pkgconfig:$(PKG_CONFIG_PATH) @@ -115,7 +115,7 @@ LDFLAGS += -rdynamic LDLIBS += -lrt endif -YOSYS_VER := 0.9+36 +YOSYS_VER := 0.9+231 GIT_REV := $(shell cd $(YOSYS_SRC) && git rev-parse --short HEAD 2> /dev/null || echo UNKNOWN) OBJS = kernel/version_$(GIT_REV).o @@ -332,6 +332,10 @@ Verilog Attributes and non-standard features that represent module parameters or localparams (when the HDL front-end is run in ``-pwires`` mode). +- Wires marked with the ``hierconn`` attribute are connected to wires with the + same name (format ``cell_name.identifier``) when they are imported from + sub-modules by ``flatten``. + - The ``clkbuf_driver`` attribute can be set on an output port of a blackbox module to mark it as a clock buffer output, and thus prevent ``clkbufmap`` from inserting another clock buffer on a net driven by such output. @@ -351,19 +355,16 @@ Verilog Attributes and non-standard features blackbox or whitebox definition to a corresponding entry in a `abc9` box-file. -- The port attribute ``abc_scc_break`` indicates a module input port that will - be treated as a primary output during `abc9` techmapping. Doing so eliminates - the possibility of a strongly-connected component (i.e. a combinatorial loop) - existing. Typically, this is specified for sequential inputs on otherwise - combinatorial boxes -- for example, applying ``abc_scc_break`` onto the `D` - port of a LUTRAM cell prevents `abc9` from interpreting any `Q` -> `D` paths - as a combinatorial loop. - - The port attribute ``abc_carry`` marks the carry-in (if an input port) and carry-out (if output port) ports of a box. This information is necessary for `abc9` to preserve the integrity of carry-chains. Specifying this attribute onto a bus port will affect only its most significant bit. +- The port attribute ``abc_arrival`` specifies an integer (for output ports + only) to be used as the arrival time of this sequential port. It can be used, + for example, to specify the clk-to-Q delay of a flip-flop for consideration + during techmapping. + - In addition to the ``(* ... *)`` attribute syntax, Yosys supports the non-standard ``{* ... *}`` attribute syntax to set default attributes for everything that comes after the ``{* ... *}`` statement. (Reset diff --git a/backends/aiger/xaiger.cc b/backends/aiger/xaiger.cc index e1b84236d..fa6ba0aca 100644 --- a/backends/aiger/xaiger.cc +++ b/backends/aiger/xaiger.cc @@ -83,6 +83,7 @@ struct XAigerWriter dict<SigBit, pair<SigBit, SigBit>> and_map; vector<std::tuple<SigBit,RTLIL::Cell*,RTLIL::IdString,int>> ci_bits; vector<std::tuple<SigBit,RTLIL::Cell*,RTLIL::IdString,int,int>> co_bits; + dict<SigBit, float> arrival_times; vector<pair<int, int>> aig_gates; vector<int> aig_outputs; @@ -247,14 +248,15 @@ struct XAigerWriter if (!holes_mode) { toposort.node(cell->name); for (const auto &conn : cell->connections()) { - if (cell->input(conn.first)) { + auto port_wire = inst_module->wire(conn.first); + if (port_wire->port_input) { // Ignore inout for the sake of topographical ordering - if (cell->output(conn.first)) continue; + if (port_wire->port_output) continue; for (auto bit : sigmap(conn.second)) bit_users[bit].insert(cell->name); } - if (cell->output(conn.first)) + if (port_wire->port_output) for (auto bit : sigmap(conn.second)) bit_drivers[bit].insert(cell->name); } @@ -271,7 +273,7 @@ struct XAigerWriter log_error("Connection '%s' on cell '%s' (type '%s') not recognised!\n", log_id(c.first), log_id(cell), log_id(cell->type)); if (is_input) { - for (auto b : c.second.bits()) { + for (auto b : c.second) { Wire *w = b.wire; if (!w) continue; if (!w->port_output || !cell_known) { @@ -287,7 +289,17 @@ struct XAigerWriter } } if (is_output) { - for (auto b : c.second.bits()) { + int arrival = 0; + if (port_wire) { + auto it = port_wire->attributes.find("\\abc_arrival"); + if (it != port_wire->attributes.end()) { + if (it->second.flags != 0) + log_error("Attribute 'abc_arrival' on port '%s' of module '%s' is not an integer.\n", log_id(port_wire), log_id(cell->type)); + arrival = it->second.as_int(); + } + } + + for (auto b : c.second) { Wire *w = b.wire; if (!w) continue; input_bits.insert(b); @@ -295,6 +307,9 @@ struct XAigerWriter if (O != b) alias_map[O] = b; undriven_bits.erase(O); + + if (arrival) + arrival_times[b] = arrival; } } } @@ -489,16 +504,16 @@ struct XAigerWriter aig_outputs.push_back(bit2aig(bit)); } + if (output_bits.empty()) { + output_bits.insert(State::S0); + omode = true; + } + for (auto bit : output_bits) { ordered_outputs[bit] = aig_o++; aig_outputs.push_back(bit2aig(bit)); } - if (output_bits.empty()) { - aig_o++; - aig_outputs.push_back(0); - omode = true; - } } void write_aiger(std::ostream &f, bool ascii_mode) @@ -560,26 +575,38 @@ struct XAigerWriter f << "c"; - if (!box_list.empty()) { - auto write_buffer = [](std::stringstream &buffer, int i32) { - int32_t i32_be = to_big_endian(i32); - buffer.write(reinterpret_cast<const char*>(&i32_be), sizeof(i32_be)); - }; - - std::stringstream h_buffer; - auto write_h_buffer = std::bind(write_buffer, std::ref(h_buffer), std::placeholders::_1); - write_h_buffer(1); - log_debug("ciNum = %d\n", GetSize(input_bits) + GetSize(ci_bits)); - write_h_buffer(input_bits.size() + ci_bits.size()); - log_debug("coNum = %d\n", GetSize(output_bits) + GetSize(co_bits)); - write_h_buffer(output_bits.size() + co_bits.size()); - log_debug("piNum = %d\n", GetSize(input_bits)); - write_h_buffer(input_bits.size()); - log_debug("poNum = %d\n", GetSize(output_bits)); - write_h_buffer(output_bits.size()); - log_debug("boxNum = %d\n", GetSize(box_list)); - write_h_buffer(box_list.size()); + log_assert(!output_bits.empty()); + auto write_buffer = [](std::stringstream &buffer, int i32) { + int32_t i32_be = to_big_endian(i32); + buffer.write(reinterpret_cast<const char*>(&i32_be), sizeof(i32_be)); + }; + std::stringstream h_buffer; + auto write_h_buffer = std::bind(write_buffer, std::ref(h_buffer), std::placeholders::_1); + write_h_buffer(1); + log_debug("ciNum = %d\n", GetSize(input_bits) + GetSize(ci_bits)); + write_h_buffer(input_bits.size() + ci_bits.size()); + log_debug("coNum = %d\n", GetSize(output_bits) + GetSize(co_bits)); + write_h_buffer(output_bits.size() + GetSize(co_bits)); + log_debug("piNum = %d\n", GetSize(input_bits)); + write_h_buffer(input_bits.size()); + log_debug("poNum = %d\n", GetSize(output_bits)); + write_h_buffer(output_bits.size()); + log_debug("boxNum = %d\n", GetSize(box_list)); + write_h_buffer(box_list.size()); + + auto write_buffer_float = [](std::stringstream &buffer, float f32) { + buffer.write(reinterpret_cast<const char*>(&f32), sizeof(f32)); + }; + std::stringstream i_buffer; + auto write_i_buffer = std::bind(write_buffer_float, std::ref(i_buffer), std::placeholders::_1); + for (auto bit : input_bits) + write_i_buffer(arrival_times.at(bit, 0)); + //std::stringstream o_buffer; + //auto write_o_buffer = std::bind(write_buffer_float, std::ref(o_buffer), std::placeholders::_1); + //for (auto bit : output_bits) + // write_o_buffer(0); + if (!box_list.empty()) { RTLIL::Module *holes_module = module->design->addModule("$__holes__"); log_assert(holes_module); @@ -643,19 +670,12 @@ struct XAigerWriter write_h_buffer(box_count++); } - f << "h"; - std::string buffer_str = h_buffer.str(); - int32_t buffer_size_be = to_big_endian(buffer_str.size()); - f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be)); - f.write(buffer_str.data(), buffer_str.size()); - std::stringstream r_buffer; auto write_r_buffer = std::bind(write_buffer, std::ref(r_buffer), std::placeholders::_1); write_r_buffer(0); - f << "r"; - buffer_str = r_buffer.str(); - buffer_size_be = to_big_endian(buffer_str.size()); + std::string buffer_str = r_buffer.str(); + int32_t buffer_size_be = to_big_endian(buffer_str.size()); f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be)); f.write(buffer_str.data(), buffer_str.size()); @@ -709,6 +729,23 @@ struct XAigerWriter } } + f << "h"; + std::string buffer_str = h_buffer.str(); + int32_t buffer_size_be = to_big_endian(buffer_str.size()); + f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be)); + f.write(buffer_str.data(), buffer_str.size()); + + f << "i"; + buffer_str = i_buffer.str(); + buffer_size_be = to_big_endian(buffer_str.size()); + f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be)); + f.write(buffer_str.data(), buffer_str.size()); + //f << "o"; + //buffer_str = o_buffer.str(); + //buffer_size_be = to_big_endian(buffer_str.size()); + //f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be)); + //f.write(buffer_str.data(), buffer_str.size()); + f << stringf("Generated by %s\n", yosys_version_str); } @@ -760,11 +797,11 @@ struct XAigerWriter f << stringf("box %d %d %s\n", box_count++, 0, log_id(cell->name)); output_lines.sort(); + if (omode) + output_lines[State::S0] = "output 0 0 $__dummy__\n"; for (auto &it : output_lines) f << it.second; log_assert(output_lines.size() == output_bits.size()); - if (omode && output_bits.empty()) - f << "output " << output_lines.size() << " 0 $__dummy__\n"; wire_lines.sort(); for (auto &it : wire_lines) diff --git a/frontends/aiger/aigerparse.cc b/frontends/aiger/aigerparse.cc index 06522939f..e8ee487e5 100644 --- a/frontends/aiger/aigerparse.cc +++ b/frontends/aiger/aigerparse.cc @@ -430,6 +430,7 @@ void AigerReader::parse_xaiger(const dict<int,IdString> &box_lookup) else if (c == 'r') { uint32_t dataSize YS_ATTRIBUTE(unused) = parse_xaiger_literal(f); flopNum = parse_xaiger_literal(f); + log_debug("flopNum: %u\n", flopNum); log_assert(dataSize == (flopNum+1) * sizeof(uint32_t)); f.ignore(flopNum * sizeof(uint32_t)); } @@ -496,8 +497,7 @@ void AigerReader::parse_aiger_ascii() // Parse latches RTLIL::Wire *clk_wire = nullptr; - if (L > 0) { - log_assert(clk_name != ""); + if (L > 0 && !clk_name.empty()) { clk_wire = module->wire(clk_name); log_assert(!clk_wire); log_debug2("Creating %s\n", clk_name.c_str()); @@ -513,7 +513,10 @@ void AigerReader::parse_aiger_ascii() RTLIL::Wire *q_wire = createWireIfNotExists(module, l1); RTLIL::Wire *d_wire = createWireIfNotExists(module, l2); - module->addDffGate(NEW_ID, clk_wire, d_wire, q_wire); + if (clk_wire) + module->addDffGate(NEW_ID, clk_wire, d_wire, q_wire); + else + module->addFfGate(NEW_ID, d_wire, q_wire); // Reset logic is optional in AIGER 1.9 if (f.peek() == ' ') { @@ -621,8 +624,7 @@ void AigerReader::parse_aiger_binary() // Parse latches RTLIL::Wire *clk_wire = nullptr; - if (L > 0) { - log_assert(clk_name != ""); + if (L > 0 && !clk_name.empty()) { clk_wire = module->wire(clk_name); log_assert(!clk_wire); log_debug2("Creating %s\n", clk_name.c_str()); @@ -638,7 +640,10 @@ void AigerReader::parse_aiger_binary() RTLIL::Wire *q_wire = createWireIfNotExists(module, l1); RTLIL::Wire *d_wire = createWireIfNotExists(module, l2); - module->addDff(NEW_ID, clk_wire, d_wire, q_wire); + if (clk_wire) + module->addDff(NEW_ID, clk_wire, d_wire, q_wire); + else + module->addFf(NEW_ID, d_wire, q_wire); // Reset logic is optional in AIGER 1.9 if (f.peek() == ' ') { @@ -776,19 +781,19 @@ void AigerReader::post_process() // NB: Assume box_module->ports are sorted alphabetically // (as RTLIL::Module::fixup_ports() would do) for (auto port_name : box_module->ports) { - RTLIL::Wire* w = box_module->wire(port_name); - log_assert(w); + RTLIL::Wire* port = box_module->wire(port_name); + log_assert(port); RTLIL::SigSpec rhs; - RTLIL::Wire* wire = nullptr; - for (int i = 0; i < GetSize(w); i++) { - if (w->port_input) { + for (int i = 0; i < GetSize(port); i++) { + RTLIL::Wire* wire = nullptr; + if (port->port_input) { log_assert(co_count < outputs.size()); wire = outputs[co_count++]; log_assert(wire); log_assert(wire->port_output); wire->port_output = false; } - if (w->port_output) { + if (port->port_output) { log_assert((piNum + ci_count) < inputs.size()); wire = inputs[piNum + ci_count++]; log_assert(wire); @@ -797,6 +802,7 @@ void AigerReader::post_process() } rhs.append(wire); } + cell->setPort(port_name, rhs); } } @@ -814,6 +820,7 @@ void AigerReader::post_process() RTLIL::Wire* wire = inputs[variable]; log_assert(wire); log_assert(wire->port_input); + log_debug("Renaming input %s", log_id(wire)); if (index == 0) { // Cope with the fact that a CI might be identical @@ -840,6 +847,7 @@ void AigerReader::post_process() wire->port_input = false; } } + log_debug(" -> %s\n", log_id(wire)); } else if (type == "output") { log_assert(static_cast<unsigned>(variable + co_count) < outputs.size()); @@ -850,6 +858,7 @@ void AigerReader::post_process() wire->port_output = false; continue; } + log_debug("Renaming output %s", log_id(wire)); if (index == 0) { // Cope with the fact that a CO might be identical @@ -871,6 +880,7 @@ void AigerReader::post_process() else { wire->port_output = false; module->connect(wire, existing); + wire = existing; } } else if (index > 0) { @@ -896,6 +906,7 @@ void AigerReader::post_process() wire->port_output = false; } } + log_debug(" -> %s\n", log_id(wire)); } else if (type == "box") { RTLIL::Cell* cell = module->cell(stringf("$__box%d__", variable)); @@ -1004,8 +1015,8 @@ struct AigerFrontend : public Frontend { log(" Name of module to be created (default: <filename>)\n"); log("\n"); log(" -clk_name <wire_name>\n"); - log(" AIGER latches to be transformed into posedge DFFs clocked by wire of"); - log(" this name (default: clk)\n"); + log(" If specified, AIGER latches to be transformed into $_DFF_P_ cells\n"); + log(" clocked by wire of this name. Otherwise, $_FF_ cells will be used.\n"); log("\n"); log(" -map <filename>\n"); log(" read file with port and latch symbols\n"); diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 86dd80c65..52fcf3ee7 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -1530,10 +1530,16 @@ skip_dynamic_range_lvalue_expansion:; current_scope[wire_en->str] = wire_en; while (wire_en->simplify(true, false, false, 1, -1, false, false)) { } - std::vector<RTLIL::State> x_bit; - x_bit.push_back(RTLIL::State::Sx); + AstNode *check_defval; + if (type == AST_LIVE || type == AST_FAIR) { + check_defval = new AstNode(AST_REDUCE_BOOL, children[0]->clone()); + } else { + std::vector<RTLIL::State> x_bit; + x_bit.push_back(RTLIL::State::Sx); + check_defval = mkconst_bits(x_bit, false); + } - AstNode *assign_check = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bit, false)); + AstNode *assign_check = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), check_defval); assign_check->children[0]->str = id_check; assign_check->children[0]->was_checked = true; @@ -1546,9 +1552,13 @@ skip_dynamic_range_lvalue_expansion:; default_signals->children.push_back(assign_en); current_top_block->children.insert(current_top_block->children.begin(), default_signals); - assign_check = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), new AstNode(AST_REDUCE_BOOL, children[0]->clone())); - assign_check->children[0]->str = id_check; - assign_check->children[0]->was_checked = true; + if (type == AST_LIVE || type == AST_FAIR) { + assign_check = nullptr; + } else { + assign_check = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), new AstNode(AST_REDUCE_BOOL, children[0]->clone())); + assign_check->children[0]->str = id_check; + assign_check->children[0]->was_checked = true; + } if (current_always == nullptr || current_always->type != AST_INITIAL) { assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(1, false, 1)); @@ -1560,7 +1570,8 @@ skip_dynamic_range_lvalue_expansion:; assign_en->children[0]->was_checked = true; newNode = new AstNode(AST_BLOCK); - newNode->children.push_back(assign_check); + if (assign_check != nullptr) + newNode->children.push_back(assign_check); newNode->children.push_back(assign_en); AstNode *assertnode = new AstNode(type); diff --git a/passes/pmgen/.gitignore b/passes/pmgen/.gitignore index 6b319b8c3..e52f3282f 100644 --- a/passes/pmgen/.gitignore +++ b/passes/pmgen/.gitignore @@ -1 +1 @@ -/*_pm.h
\ No newline at end of file +/*_pm.h diff --git a/passes/pmgen/Makefile.inc b/passes/pmgen/Makefile.inc index 6648e2ec0..fcbaa99ed 100644 --- a/passes/pmgen/Makefile.inc +++ b/passes/pmgen/Makefile.inc @@ -21,6 +21,12 @@ $(eval $(call add_extra_objs,passes/pmgen/ice40_wrapcarry_pm.h)) # -------------------------------------- +OBJS += passes/pmgen/xilinx_dsp.o +passes/pmgen/xilinx_dsp.o: passes/pmgen/xilinx_dsp_pm.h +$(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_pm.h)) + +# -------------------------------------- + OBJS += passes/pmgen/peepopt.o passes/pmgen/peepopt.o: passes/pmgen/peepopt_pm.h $(eval $(call add_extra_objs,passes/pmgen/peepopt_pm.h)) diff --git a/passes/pmgen/ice40_dsp.cc b/passes/pmgen/ice40_dsp.cc index 16bfe537f..31e11c742 100644 --- a/passes/pmgen/ice40_dsp.cc +++ b/passes/pmgen/ice40_dsp.cc @@ -29,15 +29,16 @@ void create_ice40_dsp(ice40_dsp_pm &pm) { auto &st = pm.st_ice40_dsp; -#if 0 +#if 1 log("\n"); - log("ffA: %s\n", log_id(st.ffA, "--")); - log("ffB: %s\n", log_id(st.ffB, "--")); - log("mul: %s\n", log_id(st.mul, "--")); - log("ffY: %s\n", log_id(st.ffY, "--")); - log("addAB: %s\n", log_id(st.addAB, "--")); - log("muxAB: %s\n", log_id(st.muxAB, "--")); - log("ffS: %s\n", log_id(st.ffS, "--")); + log("ffA: %s\n", log_id(st.ffA, "--")); + log("ffB: %s\n", log_id(st.ffB, "--")); + log("mul: %s\n", log_id(st.mul, "--")); + log("ffFJKG: %s\n", log_id(st.ffFJKG, "--")); + log("addAB: %s\n", log_id(st.addAB, "--")); + log("muxAB: %s\n", log_id(st.muxAB, "--")); + log("ffO_lo: %s\n", log_id(st.ffO_lo, "--")); + log("ffO_hi: %s\n", log_id(st.ffO_hi, "--")); #endif log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm.module), log_id(st.mul)); @@ -52,42 +53,42 @@ void create_ice40_dsp(ice40_dsp_pm &pm) return; } - if (GetSize(st.sigS) > 32) { - log(" accumulator (%s) is too large (%d > 32).\n", log_signal(st.sigS), GetSize(st.sigS)); + if (GetSize(st.sigO) > 33) { + log(" adder/accumulator (%s) is too large (%d > 33).\n", log_signal(st.sigO), GetSize(st.sigO)); return; } - if (GetSize(st.sigY) > 32) { - log(" output (%s) is too large (%d > 32).\n", log_signal(st.sigY), GetSize(st.sigY)); + if (GetSize(st.sigH) > 32) { + log(" output (%s) is too large (%d > 32).\n", log_signal(st.sigH), GetSize(st.sigH)); return; } - bool mul_signed = st.mul->getParam("\\A_SIGNED").as_bool(); + Cell *cell = st.mul; + if (cell->type == "$mul") { + log(" replacing %s with SB_MAC16 cell.\n", log_id(st.mul->type)); - log(" replacing $mul with SB_MAC16 cell.\n"); - - Cell *cell = pm.module->addCell(NEW_ID, "\\SB_MAC16"); - pm.module->swap_names(cell, st.mul); + cell = pm.module->addCell(NEW_ID, "\\SB_MAC16"); + pm.module->swap_names(cell, st.mul); + } + else log_assert(cell->type == "\\SB_MAC16"); // SB_MAC16 Input Interface - SigSpec A = st.sigA; - A.extend_u0(16, mul_signed); + log_assert(GetSize(A) == 16); SigSpec B = st.sigB; - B.extend_u0(16, mul_signed); + log_assert(GetSize(B) == 16); - SigSpec CD; - if (st.muxA) - CD = st.muxA->getPort("\\B"); - if (st.muxB) - CD = st.muxB->getPort("\\A"); - CD.extend_u0(32, mul_signed); + SigSpec CD = st.sigCD; + if (CD.empty()) + CD = RTLIL::Const(0, 32); + else + log_assert(GetSize(CD) == 32); cell->setPort("\\A", A); cell->setPort("\\B", B); - cell->setPort("\\C", CD.extract(0, 16)); - cell->setPort("\\D", CD.extract(16, 16)); + cell->setPort("\\C", CD.extract(16, 16)); + cell->setPort("\\D", CD.extract(0, 16)); cell->setParam("\\A_REG", st.ffA ? State::S1 : State::S0); cell->setParam("\\B_REG", st.ffB ? State::S1 : State::S0); @@ -100,7 +101,7 @@ void create_ice40_dsp(ice40_dsp_pm &pm) cell->setPort("\\IRSTTOP", State::S0); cell->setPort("\\IRSTBOT", State::S0); - if (st.clock_vld) + if (st.clock != SigBit()) { cell->setPort("\\CLK", st.clock); cell->setPort("\\CE", State::S1); @@ -114,11 +115,13 @@ void create_ice40_dsp(ice40_dsp_pm &pm) if (st.ffB) log(" ffB:%s", log_id(st.ffB)); - if (st.ffY) - log(" ffY:%s", log_id(st.ffY)); + if (st.ffFJKG) + log(" ffFJKG:%s", log_id(st.ffFJKG)); - if (st.ffS) - log(" ffS:%s", log_id(st.ffS)); + if (st.ffO_lo) + log(" ffO_lo:%s", log_id(st.ffO_lo)); + if (st.ffO_hi) + log(" ffO_hi:%s", log_id(st.ffO_hi)); log("\n"); } @@ -135,21 +138,43 @@ void create_ice40_dsp(ice40_dsp_pm &pm) cell->setPort("\\SIGNEXTOUT", pm.module->addWire(NEW_ID)); cell->setPort("\\CI", State::Sx); - cell->setPort("\\CO", pm.module->addWire(NEW_ID)); cell->setPort("\\ACCUMCI", State::Sx); cell->setPort("\\ACCUMCO", pm.module->addWire(NEW_ID)); // SB_MAC16 Output Interface - SigSpec O = st.ffS ? st.sigS : st.sigY; + SigSpec O = st.sigO; + int O_width = GetSize(O); + if (O_width == 33) { + log_assert(st.addAB); + // If we have a signed multiply-add, then perform sign extension + // TODO: Need to check CD[31:16] is sign extension of CD[15:0]? + if (st.addAB->getParam("\\A_SIGNED").as_bool() && st.addAB->getParam("\\B_SIGNED").as_bool()) + pm.module->connect(O[32], O[31]); + else + cell->setPort("\\CO", O[32]); + O.remove(O_width-1); + } + else + cell->setPort("\\CO", pm.module->addWire(NEW_ID)); + log_assert(GetSize(O) <= 32); if (GetSize(O) < 32) O.append(pm.module->addWire(NEW_ID, 32-GetSize(O))); cell->setPort("\\O", O); + bool accum = false; if (st.addAB) { - log(" accumulator %s (%s)\n", log_id(st.addAB), log_id(st.addAB->type)); + if (st.addA) + accum = (st.ffO_lo && st.ffO_hi && st.addAB->getPort("\\B") == st.sigO); + else if (st.addB) + accum = (st.ffO_lo && st.ffO_hi && st.addAB->getPort("\\A") == st.sigO); + else log_abort(); + if (accum) + log(" accumulator %s (%s)\n", log_id(st.addAB), log_id(st.addAB->type)); + else + log(" adder %s (%s)\n", log_id(st.addAB), log_id(st.addAB->type)); cell->setPort("\\ADDSUBTOP", st.addAB->type == "$add" ? State::S0 : State::S1); cell->setPort("\\ADDSUBBOT", st.addAB->type == "$add" ? State::S0 : State::S1); } else { @@ -177,28 +202,39 @@ void create_ice40_dsp(ice40_dsp_pm &pm) cell->setParam("\\C_REG", State::S0); cell->setParam("\\D_REG", State::S0); - cell->setParam("\\TOP_8x8_MULT_REG", st.ffY ? State::S1 : State::S0); - cell->setParam("\\BOT_8x8_MULT_REG", st.ffY ? State::S1 : State::S0); - cell->setParam("\\PIPELINE_16x16_MULT_REG1", st.ffY ? State::S1 : State::S0); + cell->setParam("\\TOP_8x8_MULT_REG", st.ffFJKG ? State::S1 : State::S0); + cell->setParam("\\BOT_8x8_MULT_REG", st.ffFJKG ? State::S1 : State::S0); + cell->setParam("\\PIPELINE_16x16_MULT_REG1", st.ffFJKG ? State::S1 : State::S0); cell->setParam("\\PIPELINE_16x16_MULT_REG2", State::S0); - cell->setParam("\\TOPOUTPUT_SELECT", Const(st.ffS ? 1 : 3, 2)); + cell->setParam("\\TOPOUTPUT_SELECT", Const(st.ffO_hi ? 1 : (st.addAB ? 0 : 3), 2)); cell->setParam("\\TOPADDSUB_LOWERINPUT", Const(2, 2)); - cell->setParam("\\TOPADDSUB_UPPERINPUT", State::S0); + cell->setParam("\\TOPADDSUB_UPPERINPUT", accum ? State::S0 : State::S1); cell->setParam("\\TOPADDSUB_CARRYSELECT", Const(3, 2)); - cell->setParam("\\BOTOUTPUT_SELECT", Const(st.ffS ? 1 : 3, 2)); + cell->setParam("\\BOTOUTPUT_SELECT", Const(st.ffO_lo ? 1 : (st.addAB ? 0 : 3), 2)); cell->setParam("\\BOTADDSUB_LOWERINPUT", Const(2, 2)); - cell->setParam("\\BOTADDSUB_UPPERINPUT", State::S0); + cell->setParam("\\BOTADDSUB_UPPERINPUT", accum ? State::S0 : State::S1); cell->setParam("\\BOTADDSUB_CARRYSELECT", Const(0, 2)); cell->setParam("\\MODE_8x8", State::S0); - cell->setParam("\\A_SIGNED", mul_signed ? State::S1 : State::S0); - cell->setParam("\\B_SIGNED", mul_signed ? State::S1 : State::S0); + cell->setParam("\\A_SIGNED", st.mul->getParam("\\A_SIGNED").as_bool()); + cell->setParam("\\B_SIGNED", st.mul->getParam("\\B_SIGNED").as_bool()); - pm.autoremove(st.mul); - pm.autoremove(st.ffY); - pm.autoremove(st.ffS); + if (cell != st.mul) + pm.autoremove(st.mul); + else + pm.blacklist(st.mul); + pm.autoremove(st.ffFJKG); + pm.autoremove(st.addAB); + if (st.ffO_lo) { + SigSpec O = st.sigO.extract(0,std::min(16,st.ffO_lo->getParam("\\WIDTH").as_int())); + st.ffO_lo->connections_.at("\\Q").replace(O, pm.module->addWire(NEW_ID, GetSize(O))); + } + if (st.ffO_hi) { + SigSpec O = st.sigO.extract_end(16); + st.ffO_hi->connections_.at("\\Q").replace(O, pm.module->addWire(NEW_ID, GetSize(O))); + } } struct Ice40DspPass : public Pass { diff --git a/passes/pmgen/ice40_dsp.pmg b/passes/pmgen/ice40_dsp.pmg index 7003092bb..8221cdb69 100644 --- a/passes/pmgen/ice40_dsp.pmg +++ b/passes/pmgen/ice40_dsp.pmg @@ -1,87 +1,128 @@ pattern ice40_dsp state <SigBit> clock -state <bool> clock_pol clock_vld -state <SigSpec> sigA sigB sigY sigS +state <bool> clock_pol +state <std::set<SigBit>> sigAset sigBset +state <SigSpec> sigA sigB sigCD sigH sigO sigOused state <Cell*> addAB muxAB match mul - select mul->type.in($mul) + select mul->type.in($mul, \SB_MAC16) select GetSize(mul->getPort(\A)) + GetSize(mul->getPort(\B)) > 10 - select GetSize(mul->getPort(\Y)) > 10 endmatch +code sigAset sigBset + SigSpec A = port(mul, \A); + A.remove_const(); + sigAset = A.to_sigbit_set(); + SigSpec B = port(mul, \B); + B.remove_const(); + sigBset = B.to_sigbit_set(); +endcode + +code sigH + if (mul->type == $mul) + sigH = mul->getPort(\Y); + else if (mul->type == \SB_MAC16) + sigH = mul->getPort(\O); + else log_abort(); + if (GetSize(sigH) <= 10) + reject; +endcode + match ffA + if mul->type != \SB_MAC16 || !param(mul, \A_REG).as_bool() + if !sigAset.empty() select ffA->type.in($dff) - // select nusers(port(ffA, \Q)) == 2 - index <SigSpec> port(ffA, \Q) === port(mul, \A) optional endmatch -code sigA clock clock_pol clock_vld +code sigA clock clock_pol sigA = port(mul, \A); if (ffA) { - sigA = port(ffA, \D); + auto ffAset = port(ffA, \Q).to_sigbit_set(); + if (!std::includes(ffAset.begin(), ffAset.end(), sigAset.begin(), sigAset.end())) + reject; + + for (auto b : port(ffA, \Q)) + if (b.wire->get_bool_attribute(\keep)) + reject; clock = port(ffA, \CLK).as_bit(); clock_pol = param(ffA, \CLK_POLARITY).as_bool(); - clock_vld = true; + + sigA.replace(port(ffA, \Q), port(ffA, \D)); } endcode match ffB + if mul->type != \SB_MAC16 || !param(mul, \B_REG).as_bool() + if !sigBset.empty() select ffB->type.in($dff) - // select nusers(port(ffB, \Q)) == 2 - index <SigSpec> port(ffB, \Q) === port(mul, \B) optional endmatch -code sigB clock clock_pol clock_vld +code sigB clock clock_pol sigB = port(mul, \B); if (ffB) { - sigB = port(ffB, \D); + auto ffBset = port(ffB, \Q).to_sigbit_set(); + if (!std::includes(ffBset.begin(), ffBset.end(), sigBset.begin(), sigBset.end())) + reject; + + for (auto b : port(ffB, \Q)) + if (b.wire->get_bool_attribute(\keep)) + reject; + SigBit c = port(ffB, \CLK).as_bit(); bool cp = param(ffB, \CLK_POLARITY).as_bool(); - if (clock_vld && (c != clock || cp != clock_pol)) + if (clock != SigBit() && (c != clock || cp != clock_pol)) reject; clock = c; clock_pol = cp; - clock_vld = true; + + sigB.replace(port(ffB, \Q), port(ffB, \D)); } endcode -match ffY - select ffY->type.in($dff) - select nusers(port(ffY, \D)) == 2 - index <SigSpec> port(ffY, \D) === port(mul, \Y) +match ffFJKG + // Ensure pipeline register is not already used + if mul->type != \SB_MAC16 || (!param(mul, \TOP_8x8_MULT_REG).as_bool() && !param(mul, \BOT_8x8_MULT_REG).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG1).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG2).as_bool()) + select ffFJKG->type.in($dff) + select nusers(port(ffFJKG, \D)) == 2 + index <SigSpec> port(ffFJKG, \D) === sigH optional endmatch -code sigY clock clock_pol clock_vld - sigY = port(mul, \Y); +code sigH sigO clock clock_pol + if (ffFJKG) { + sigH = port(ffFJKG, \Q); + for (auto b : sigH) + if (b.wire->get_bool_attribute(\keep)) + reject; - if (ffY) { - sigY = port(ffY, \Q); - SigBit c = port(ffY, \CLK).as_bit(); - bool cp = param(ffY, \CLK_POLARITY).as_bool(); + SigBit c = port(ffFJKG, \CLK).as_bit(); + bool cp = param(ffFJKG, \CLK_POLARITY).as_bool(); - if (clock_vld && (c != clock || cp != clock_pol)) + if (clock != SigBit() && (c != clock || cp != clock_pol)) reject; clock = c; clock_pol = cp; - clock_vld = true; } + + sigO = sigH; endcode match addA select addA->type.in($add) select nusers(port(addA, \A)) == 2 - index <SigSpec> port(addA, \A) === sigY + filter param(addA, \A_WIDTH).as_int() <= GetSize(sigH) + //index <SigSpec> port(addA, \A) === sigH.extract(0, param(addA, \A_WIDTH).as_int()) + filter port(addA, \A) == sigH.extract(0, param(addA, \A_WIDTH).as_int()) optional endmatch @@ -89,75 +130,174 @@ match addB if !addA select addB->type.in($add, $sub) select nusers(port(addB, \B)) == 2 - index <SigSpec> port(addB, \B) === sigY + filter param(addB, \B_WIDTH).as_int() <= GetSize(sigH) + //index <SigSpec> port(addB, \B) === sigH.extract(0, param(addB, \B_WIDTH).as_int()) + filter port(addB, \B) == sigH.extract(0, param(addB, \B_WIDTH).as_int()) optional endmatch -code addAB sigS +code addAB sigCD sigO + bool CD_SIGNED = false; if (addA) { addAB = addA; - sigS = port(addA, \B); + sigCD = port(addAB, \B); + CD_SIGNED = param(addAB, \B_SIGNED).as_bool(); } if (addB) { addAB = addB; - sigS = port(addB, \A); + sigCD = port(addAB, \A); + CD_SIGNED = param(addAB, \A_SIGNED).as_bool(); } if (addAB) { + if (mul->type == \SB_MAC16) { + // Ensure that adder is not used + if (param(mul, \TOPOUTPUT_SELECT).as_int() != 3 || + param(mul, \BOTOUTPUT_SELECT).as_int() != 3) + reject; + } + int natural_mul_width = GetSize(sigA) + GetSize(sigB); - int actual_mul_width = GetSize(sigY); - int actual_acc_width = GetSize(sigS); + int actual_mul_width = GetSize(sigH); + int actual_acc_width = GetSize(sigCD); if ((actual_acc_width > actual_mul_width) && (natural_mul_width > actual_mul_width)) reject; - if ((actual_acc_width != actual_mul_width) && (param(mul, \A_SIGNED).as_bool() != param(addAB, \A_SIGNED).as_bool())) + // If accumulator, check adder width and signedness + if (sigCD == sigH && (actual_acc_width != actual_mul_width) && (param(mul, \A_SIGNED).as_bool() != param(addAB, \A_SIGNED).as_bool())) reject; + + sigO = port(addAB, \Y); + sigCD.extend_u0(32, CD_SIGNED); } endcode match muxA - if addAB select muxA->type.in($mux) - select nusers(port(muxA, \A)) == 2 - index <SigSpec> port(muxA, \A) === port(addAB, \Y) + index <int> nusers(port(muxA, \A)) === 2 + index <SigSpec> port(muxA, \A) === sigO optional endmatch match muxB - if addAB if !muxA select muxB->type.in($mux) - select nusers(port(muxB, \B)) == 2 - index <SigSpec> port(muxB, \B) === port(addAB, \Y) + index <int> nusers(port(muxB, \B)) === 2 + index <SigSpec> port(muxB, \B) === sigO optional endmatch code muxAB - muxAB = addAB; if (muxA) muxAB = muxA; - if (muxB) + else if (muxB) muxAB = muxB; endcode -match ffS - if muxAB - select ffS->type.in($dff) - select nusers(port(ffS, \D)) == 2 - index <SigSpec> port(ffS, \D) === port(muxAB, \Y) - index <SigSpec> port(ffS, \Q) === sigS +// Extract the bits of P that actually have a consumer +// (as opposed to being a dummy) +code sigOused + for (int i = 0; i < GetSize(sigO); i++) + if (!sigO[i].wire || nusers(sigO[i]) == 1) + sigOused.append(State::Sx); + else + sigOused.append(sigO[i]); +endcode + +match ffO_lo + if nusers(sigOused.extract(0,std::min(16,GetSize(sigOused)))) == 2 + select ffO_lo->type.in($dff) + optional endmatch -code clock clock_pol clock_vld - if (ffS) { - SigBit c = port(ffS, \CLK).as_bit(); - bool cp = param(ffS, \CLK_POLARITY).as_bool(); +code + if (ffO_lo) { + SigSpec O = sigOused.extract(0,std::min(16,param(ffO_lo, \WIDTH).as_int())); + O.remove_const(); + auto ffO_loSet = port(ffO_lo, \D).to_sigbit_set(); + auto Oset = O.to_sigbit_set(); + if (!std::includes(ffO_loSet.begin(), ffO_loSet.end(), Oset.begin(), Oset.end())) + reject; + } +endcode - if (clock_vld && (c != clock || cp != clock_pol)) +match ffO_hi + if GetSize(sigOused) > 16 + if nusers(sigOused.extract_end(16)) == 2 + select ffO_hi->type.in($dff) + optional +endmatch + +code + if (ffO_hi) { + SigSpec O = sigOused.extract_end(16); + O.remove_const(); + auto ffO_hiSet = port(ffO_hi, \D).to_sigbit_set(); + auto Oset = O.to_sigbit_set(); + if (!std::includes(ffO_hiSet.begin(), ffO_hiSet.end(), Oset.begin(), Oset.end())) reject; + } +endcode - clock = c; - clock_pol = cp; - clock_vld = true; +code clock clock_pol sigO sigCD + if (ffO_lo || ffO_hi) { + if (mul->type == \SB_MAC16) { + // Ensure that register is not already used + if (param(mul, \TOPOUTPUT_SELECT).as_int() == 1 || + param(mul, \BOTOUTPUT_SELECT).as_int() == 1) + reject; + + // Ensure that OLOADTOP/OLOADBOT is unused or zero + if ((mul->hasPort(\OLOADTOP) && !port(mul, \OLOADTOP).is_fully_zero()) + || (mul->hasPort(\OLOADBOT) && !port(mul, \OLOADBOT).is_fully_zero())) + reject; + } + + if (ffO_lo) { + for (auto b : port(ffO_lo, \Q)) + if (b.wire->get_bool_attribute(\keep)) + reject; + + SigBit c = port(ffO_lo, \CLK).as_bit(); + bool cp = param(ffO_lo, \CLK_POLARITY).as_bool(); + + if (clock != SigBit() && (c != clock || cp != clock_pol)) + reject; + + clock = c; + clock_pol = cp; + + sigO.replace(port(ffO_lo, \D), port(ffO_lo, \Q)); + } + + if (ffO_hi) { + for (auto b : port(ffO_hi, \Q)) + if (b.wire->get_bool_attribute(\keep)) + reject; + + SigBit c = port(ffO_hi, \CLK).as_bit(); + bool cp = param(ffO_hi, \CLK_POLARITY).as_bool(); + + if (clock != SigBit() && (c != clock || cp != clock_pol)) + reject; + + clock = c; + clock_pol = cp; + + sigO.replace(port(ffO_hi, \D), port(ffO_hi, \Q)); + } + + // Loading value into output register is not + // supported unless using accumulator + if (muxAB) { + if (sigCD != sigO) + reject; + if (muxA) + sigCD = port(muxAB, \B); + else if (muxB) + sigCD = port(muxAB, \A); + else log_abort(); + sigCD.extend_u0(32, addAB && param(addAB, \A_SIGNED).as_bool() && param(addAB, \B_SIGNED).as_bool()); + } } accept; endcode diff --git a/passes/pmgen/xilinx_dsp.cc b/passes/pmgen/xilinx_dsp.cc new file mode 100644 index 000000000..9291c2dfb --- /dev/null +++ b/passes/pmgen/xilinx_dsp.cc @@ -0,0 +1,243 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +#include "passes/pmgen/xilinx_dsp_pm.h" + +void pack_xilinx_dsp(dict<SigBit, Cell*> &bit_to_driver, xilinx_dsp_pm &pm) +{ + auto &st = pm.st_xilinx_dsp; + +#if 1 + log("\n"); + log("ffA: %s\n", log_id(st.ffA, "--")); + log("ffAmux: %s\n", log_id(st.ffAmux, "--")); + log("ffB: %s\n", log_id(st.ffB, "--")); + log("ffBmux: %s\n", log_id(st.ffBmux, "--")); + log("dsp: %s\n", log_id(st.dsp, "--")); + log("ffM: %s\n", log_id(st.ffM, "--")); + log("ffMmux: %s\n", log_id(st.ffMmux, "--")); + log("postAdd: %s\n", log_id(st.postAdd, "--")); + log("postAddMux: %s\n", log_id(st.postAddMux, "--")); + log("ffP: %s\n", log_id(st.ffP, "--")); + log("ffPmux: %s\n", log_id(st.ffPmux, "--")); +#endif + + log("Analysing %s.%s for Xilinx DSP packing.\n", log_id(pm.module), log_id(st.dsp)); + + Cell *cell = st.dsp; + bit_to_driver.insert(std::make_pair(cell->getPort("\\P")[17], cell)); + SigSpec C = st.sigC; + SigSpec P = st.sigP; + + if (st.postAdd) { + log(" adder %s (%s)\n", log_id(st.postAdd), log_id(st.postAdd->type)); + + SigSpec &opmode = cell->connections_.at("\\OPMODE"); + if (st.postAddMux) { + log_assert(st.ffP); + opmode[4] = st.postAddMux->getPort("\\S"); + pm.autoremove(st.postAddMux); + } + else if (st.ffP && C == P) { + C = SigSpec(); + opmode[4] = State::S0; + } + else + opmode[4] = State::S1; + opmode[6] = State::S0; + opmode[5] = State::S1; + + pm.autoremove(st.postAdd); + } + + if (st.clock != SigBit()) + { + cell->setPort("\\CLK", st.clock); + + if (st.ffA) { + SigSpec A = cell->getPort("\\A"); + SigSpec D = st.ffA->getPort("\\D"); + SigSpec Q = pm.sigmap(st.ffA->getPort("\\Q")); + A.replace(Q, D); + if (st.ffAmux) { + SigSpec Y = st.ffAmux->getPort("\\Y"); + SigSpec AB = st.ffAmux->getPort(st.ffAmuxAB == "\\A" ? "\\B" : "\\A"); + A.replace(Y, AB); + cell->setPort("\\CEA2", st.ffAmux->getPort("\\S")); + } + else + cell->setPort("\\CEA2", State::S1); + cell->setPort("\\A", A); + + cell->setParam("\\AREG", 1); + } + if (st.ffB) { + SigSpec B = cell->getPort("\\B"); + SigSpec D = st.ffB->getPort("\\D"); + SigSpec Q = st.ffB->getPort("\\Q"); + B.replace(Q, D); + if (st.ffBmux) { + SigSpec Y = st.ffBmux->getPort("\\Y"); + SigSpec AB = st.ffBmux->getPort(st.ffBmuxAB == "\\A" ? "\\B" : "\\A"); + B.replace(Y, AB); + cell->setPort("\\CEB2", st.ffBmux->getPort("\\S")); + } + else + cell->setPort("\\CEB2", State::S1); + cell->setPort("\\B", B); + + cell->setParam("\\BREG", 1); + } + if (st.ffM) { + if (st.ffMmux) { + cell->setPort("\\CEM", st.ffMmux->getPort("\\S")); + pm.autoremove(st.ffMmux); + } + else + cell->setPort("\\CEM", State::S1); + SigSpec D = st.ffM->getPort("\\D"); + SigSpec Q = st.ffM->getPort("\\Q"); + P.replace(pm.sigmap(D), Q); + + cell->setParam("\\MREG", State::S1); + pm.autoremove(st.ffM); + } + if (st.ffP) { + if (st.ffPmux) { + cell->setPort("\\CEP", st.ffPmux->getPort("\\S")); + st.ffPmux->connections_.at("\\Y").replace(P, pm.module->addWire(NEW_ID, GetSize(P))); + } + else + cell->setPort("\\CEP", State::S1); + SigSpec D = st.ffP->getPort("\\D"); + SigSpec Q = st.ffP->getPort("\\Q"); + P.replace(pm.sigmap(D), Q); + st.ffP->connections_.at("\\Q").replace(P, pm.module->addWire(NEW_ID, GetSize(P))); + + cell->setParam("\\PREG", State::S1); + } + + log(" clock: %s (%s)", log_signal(st.clock), "posedge"); + + if (st.ffA) + log(" ffA:%s", log_id(st.ffA)); + + if (st.ffB) + log(" ffB:%s", log_id(st.ffB)); + + if (st.ffM) + log(" ffM:%s", log_id(st.ffM)); + + if (st.ffP) + log(" ffP:%s", log_id(st.ffP)); + + log("\n"); + } + + if (!C.empty()) { + if (GetSize(C) < 48) + C.extend_u0(48, true); + cell->setPort("\\C", C); + } + + if (GetSize(P) < 48) + P.append(pm.module->addWire(NEW_ID, 48-GetSize(P))); + cell->setPort("\\P", P); + + pm.blacklist(cell); +} + +struct XilinxDspPass : public Pass { + XilinxDspPass() : Pass("xilinx_dsp", "Xilinx: pack DSP registers") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" xilinx_dsp [options] [selection]\n"); + log("\n"); + log("Pack registers into Xilinx DSPs\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing XILINX_DSP pass (pack DSPs).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + // if (args[argidx] == "-singleton") { + // singleton_mode = true; + // continue; + // } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) { + xilinx_dsp_pm pm(module, module->selected_cells()); + dict<SigBit, Cell*> bit_to_driver; + auto f = [&bit_to_driver](xilinx_dsp_pm &pm){ pack_xilinx_dsp(bit_to_driver, pm); }; + pm.run_xilinx_dsp(f); + + // Look for ability to convert C input from another DSP into PCIN + // NB: Needs to be done after pattern matcher has folded all + // $add cells into the DSP + for (auto cell : module->cells()) { + if (cell->type != "\\DSP48E1") + continue; + SigSpec &opmode = cell->connections_.at("\\OPMODE"); + if (opmode.extract(4,3) != Const::from_string("011")) + continue; + SigSpec C = pm.sigmap(cell->getPort("\\C")); + if (C.has_const()) + continue; + auto it = bit_to_driver.find(C[0]); + if (it == bit_to_driver.end()) + continue; + auto driver = it->second; + + // Unextend C + int i; + for (i = GetSize(C)-1; i > 0; i--) + if (C[i] != C[i-1]) + break; + if (i > 48-17) + continue; + if (driver->getPort("\\P").extract(17, i) == C.extract(0, i)) { + cell->setPort("\\C", Const(0, 48)); + Wire *cascade = module->addWire(NEW_ID, 48); + driver->setPort("\\PCOUT", cascade); + cell->setPort("\\PCIN", cascade); + opmode[6] = State::S1; + opmode[5] = State::S0; + opmode[4] = State::S1; + bit_to_driver.erase(it); + } + } + } + } +} XilinxDspPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/pmgen/xilinx_dsp.pmg b/passes/pmgen/xilinx_dsp.pmg new file mode 100644 index 000000000..fa490146c --- /dev/null +++ b/passes/pmgen/xilinx_dsp.pmg @@ -0,0 +1,273 @@ +pattern xilinx_dsp + +state <SigBit> clock +state <SigSpec> sigA sigffAmux sigB sigffBmux sigC sigM sigP +state <IdString> ffAmuxAB ffBmuxAB ffMmuxAB ffPmuxAB postAddAB postAddMuxAB + +match dsp + select dsp->type.in(\DSP48E1) +endmatch + +code sigA sigffAmux sigB sigffBmux sigM + sigA = port(dsp, \A); + int i; + for (i = GetSize(sigA)-1; i > 0; i--) + if (sigA[i] != sigA[i-1]) + break; + // Do not remove non-const sign bit + if (sigA[i].wire) + ++i; + sigA.remove(i, GetSize(sigA)-i); + sigB = port(dsp, \B); + for (i = GetSize(sigB)-1; i > 0; i--) + if (sigB[i] != sigB[i-1]) + break; + // Do not remove non-const sign bit + if (sigB[i].wire) + ++i; + sigB.remove(i, GetSize(sigB)-i); + + SigSpec P = port(dsp, \P); + // Only care about those bits that are used + for (i = 0; i < GetSize(P); i++) { + if (nusers(P[i]) <= 1) + break; + sigM.append(P[i]); + } + log_assert(nusers(P.extract_end(i)) <= 1); + //if (GetSize(sigM) <= 10) + // reject; +endcode + +match ffA + if param(dsp, \AREG).as_int() == 0 + select ffA->type.in($dff) + // DSP48E1 does not support clock inversion + select param(ffA, \CLK_POLARITY).as_bool() + filter GetSize(port(ffA, \Q)) >= GetSize(sigA) + slice offset GetSize(port(ffA, \Q)) + filter offset+GetSize(sigA) <= GetSize(port(ffA, \Q)) && port(ffA, \Q).extract(offset, GetSize(sigA)) == sigA + optional +endmatch + +code sigA sigffAmux clock + if (ffA) { + for (auto b : port(ffA, \Q)) + if (b.wire->get_bool_attribute(\keep)) + reject; + + clock = port(ffA, \CLK).as_bit(); + + sigffAmux = sigA; + sigA.replace(port(ffA, \Q), port(ffA, \D)); + } +endcode + +match ffAmux + if ffA + select ffAmux->type.in($mux) + filter GetSize(port(ffAmux, \Y)) >= GetSize(sigA) + slice offset GetSize(port(ffAmux, \Y)) + filter offset+GetSize(sigA) <= GetSize(port(ffAmux, \Y)) && port(ffAmux, \Y).extract(offset, GetSize(sigA)) == sigA + choice <IdString> AB {\A, \B} + filter offset+GetSize(sigffAmux) <= GetSize(port(ffAmux, \Y)) && port(ffAmux, AB).extract(offset, GetSize(sigffAmux)) == sigffAmux + set ffAmuxAB AB + semioptional +endmatch + +match ffB + if param(dsp, \BREG).as_int() == 0 + select ffB->type.in($dff) + // DSP48E1 does not support clock inversion + select param(ffB, \CLK_POLARITY).as_bool() + filter GetSize(port(ffB, \Q)) >= GetSize(sigB) + slice offset GetSize(port(ffB, \Q)) + filter offset+GetSize(sigB) <= GetSize(port(ffB, \Q)) && port(ffB, \Q).extract(offset, GetSize(sigB)) == sigB + optional +endmatch + +code sigB sigffBmux clock + if (ffB) { + for (auto b : port(ffB, \Q)) + if (b.wire->get_bool_attribute(\keep)) + reject; + + SigBit c = port(ffB, \CLK).as_bit(); + + if (clock != SigBit() && c != clock) + reject; + + clock = c; + + sigffBmux = sigB; + sigB.replace(port(ffB, \Q), port(ffB, \D)); + } +endcode + +match ffBmux + if ffB + select ffBmux->type.in($mux) + filter GetSize(port(ffBmux, \Y)) >= GetSize(sigB) + slice offset GetSize(port(ffBmux, \Y)) + filter offset+GetSize(sigB) <= GetSize(port(ffBmux, \Y)) && port(ffBmux, \Y).extract(offset, GetSize(sigB)) == sigB + choice <IdString> AB {\A, \B} + filter offset+GetSize(sigffBmux) <= GetSize(port(ffBmux, \Y)) && port(ffBmux, AB).extract(offset, GetSize(sigffBmux)) == sigffBmux + set ffBmuxAB AB + semioptional +endmatch + +match ffMmux + select ffMmux->type.in($mux) + select nusers(port(ffMmux, \Y)) == 2 + filter GetSize(port(ffMmux, \Y)) <= GetSize(sigM) + choice <IdString> AB {\A, \B} + filter port(ffMmux, AB) == sigM.extract(0, GetSize(port(ffMmux, \Y))) + filter nusers(sigM.extract_end(GetSize(port(ffMmux, AB)))) <= 1 + set ffMmuxAB AB + optional +endmatch + +code sigM + if (ffMmux) + sigM = port(ffMmux, \Y); +endcode + +match ffM + if param(dsp, \MREG).as_int() == 0 + select ffM->type.in($dff) + // DSP48E1 does not support clock inversion + select param(ffM, \CLK_POLARITY).as_bool() + select nusers(port(ffM, \D)) == 2 + filter GetSize(port(ffM, \D)) <= GetSize(sigM) + filter port(ffM, \D) == sigM.extract(0, GetSize(port(ffM, \D))) + filter nusers(sigM.extract_end(GetSize(port(ffM, \D)))) <= 1 + // Check ffMmux (when present) is a $dff enable mux + filter !ffMmux || port(ffM, \Q) == port(ffMmux, ffMmuxAB == \A ? \B : \A) + optional +endmatch + +code clock sigM sigP + if (ffM) { + sigM = port(ffM, \Q); + + for (auto b : sigM) + if (b.wire->get_bool_attribute(\keep)) + reject; + + SigBit c = port(ffM, \CLK).as_bit(); + + if (clock != SigBit() && c != clock) + reject; + + clock = c; + } + // Cannot have ffMmux enable mux without ffM + else if (ffMmux) + reject; + + sigP = sigM; +endcode + +match postAdd + // Ensure that Z mux is not already used + if port(dsp, \OPMODE).extract(4,3).is_fully_zero() + + select postAdd->type.in($add) + choice <IdString> AB {\A, \B} + select nusers(port(postAdd, AB)) <= 3 + filter ffMmux || nusers(port(postAdd, AB)) == 2 + filter !ffMmux || nusers(port(postAdd, AB)) == 3 + filter GetSize(port(postAdd, AB)) <= GetSize(sigP) + filter port(postAdd, AB) == sigP.extract(0, GetSize(port(postAdd, AB))) + filter nusers(sigP.extract_end(GetSize(port(postAdd, AB)))) <= 1 + set postAddAB AB + optional +endmatch + +code sigC sigP + if (postAdd) { + sigC = port(postAdd, postAddAB == \A ? \B : \A); + + // TODO for DSP48E1, which will have sign extended inputs/outputs + //int natural_mul_width = GetSize(port(dsp, \A)) + GetSize(port(dsp, \B)); + //int actual_mul_width = GetSize(sigP); + //int actual_acc_width = GetSize(sigC); + + //if ((actual_acc_width > actual_mul_width) && (natural_mul_width > actual_mul_width)) + // reject; + //if ((actual_acc_width != actual_mul_width) && (param(dsp, \A_SIGNED).as_bool() != param(postAdd, \A_SIGNED).as_bool())) + // reject; + + sigP = port(postAdd, \Y); + } +endcode + +match ffPmux + select ffPmux->type.in($mux) + select nusers(port(ffPmux, \Y)) == 2 + filter GetSize(port(ffPmux, \Y)) <= GetSize(sigP) + choice <IdString> AB {\A, \B} + filter port(ffPmux, AB) == sigP.extract(0, GetSize(port(ffPmux, \Y))) + filter nusers(sigP.extract_end(GetSize(port(ffPmux, AB)))) <= 1 + set ffPmuxAB AB + optional +endmatch + +code sigP + if (ffPmux) + sigP.replace(port(ffPmux, ffPmuxAB), port(ffPmux, \Y)); +endcode + +match ffP + if param(dsp, \PREG).as_int() == 0 + select ffP->type.in($dff) + // DSP48E1 does not support clock inversion + select param(ffP, \CLK_POLARITY).as_bool() + filter GetSize(port(ffP, \D)) >= GetSize(sigP) + slice offset GetSize(port(ffP, \D)) + filter offset+GetSize(sigP) <= GetSize(port(ffP, \D)) && port(ffP, \D).extract(offset, GetSize(sigP)) == sigP + // Check ffPmux (when present) is a $dff enable mux + filter !ffPmux || port(ffP, \Q) == port(ffPmux, ffPmuxAB == \A ? \B : \A) + optional +endmatch + +code ffP sigP clock + if (ffP) { + for (auto b : port(ffP, \Q)) + if (b.wire->get_bool_attribute(\keep)) + reject; + + SigBit c = port(ffP, \CLK).as_bit(); + + if (clock != SigBit() && c != clock) + reject; + + clock = c; + + sigP.replace(port(ffP, \D), port(ffP, \Q)); + } + // Cannot have ffPmux enable mux without ffP + else if (ffPmux) + reject; +endcode + +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 diff --git a/passes/techmap/abc9.cc b/passes/techmap/abc9.cc index 6fdf987f0..7eac08d17 100644 --- a/passes/techmap/abc9.cc +++ b/passes/techmap/abc9.cc @@ -76,8 +76,7 @@ inline std::string remap_name(RTLIL::IdString abc_name) return stringf("$abc$%d$%s", map_autoidx, abc_name.c_str()+1); } -void handle_loops(RTLIL::Design *design, - const dict<IdString,pool<IdString>> &scc_break_inputs) +void handle_loops(RTLIL::Design *design) { Pass::call(design, "scc -set_attr abc_scc_id {}"); @@ -114,30 +113,6 @@ void handle_loops(RTLIL::Design *design, } cell->attributes.erase(it); } - - auto jt = scc_break_inputs.find(cell->type); - if (jt != scc_break_inputs.end()) - for (auto port_name : jt->second) { - RTLIL::SigSpec sig; - auto &rhs = cell->connections_.at(port_name); - for (auto b : rhs) { - Wire *w = b.wire; - if (!w) continue; - w->port_output = true; - w->set_bool_attribute(ID(abc_scc_break)); - w = module->wire(stringf("%s.abci", w->name.c_str())); - if (!w) { - w = module->addWire(stringf("%s.abci", b.wire->name.c_str()), GetSize(b.wire)); - w->port_input = true; - } - else { - log_assert(b.offset < GetSize(w)); - log_assert(w->port_input); - } - sig.append(RTLIL::SigBit(w, b.offset)); - } - rhs = sig; - } } module->fixup_ports(); @@ -272,8 +247,7 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri bool cleanup, vector<int> lut_costs, bool dff_mode, std::string clk_str, bool /*keepff*/, std::string delay_target, std::string /*lutin_shared*/, bool fast_mode, bool show_tempdir, std::string box_file, std::string lut_file, - std::string wire_delay, const dict<int,IdString> &box_lookup, - const dict<IdString,pool<IdString>> &scc_break_inputs + std::string wire_delay, const dict<int,IdString> &box_lookup ) { module = current_module; @@ -413,7 +387,7 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri RTLIL::Selection& sel = design->selection_stack.back(); sel.select(module); - handle_loops(design, scc_break_inputs); + handle_loops(design); Pass::call(design, "aigmap"); @@ -1050,9 +1024,6 @@ struct Abc9Pass : public Pass { } if (arg == "-box" && argidx+1 < args.size()) { box_file = args[++argidx]; - rewrite_filename(box_file); - if (!box_file.empty() && !is_absolute_path(box_file)) - box_file = std::string(pwd) + "/" + box_file; continue; } if (arg == "-W" && argidx+1 < args.size()) { @@ -1063,8 +1034,15 @@ struct Abc9Pass : public Pass { } extra_args(args, argidx, design); + // ABC expects a box file for XAIG + if (box_file.empty()) + box_file = "+/dummy.box"; + + rewrite_filename(box_file); + if (!box_file.empty() && !is_absolute_path(box_file)) + box_file = std::string(pwd) + "/" + box_file; + dict<int,IdString> box_lookup; - dict<IdString,pool<IdString>> scc_break_inputs; for (auto m : design->modules()) { auto it = m->attributes.find(ID(abc_box_id)); if (it == m->attributes.end()) @@ -1082,17 +1060,13 @@ struct Abc9Pass : public Pass { for (auto p : m->ports) { auto w = m->wire(p); log_assert(w); - if (w->port_input) { - if (w->attributes.count(ID(abc_scc_break))) - scc_break_inputs[m->name].insert(p); - if (w->attributes.count(ID(abc_carry))) { + if (w->attributes.count(ID(abc_carry))) { + if (w->port_input) { if (carry_in) log_error("Module '%s' contains more than one 'abc_carry' input port.\n", log_id(m)); carry_in = w; } - } - if (w->port_output) { - if (w->attributes.count(ID(abc_carry))) { + else if (w->port_output) { if (carry_out) log_error("Module '%s' contains more than one 'abc_carry' input port.\n", log_id(m)); carry_out = w; @@ -1144,7 +1118,7 @@ struct Abc9Pass : public Pass { if (!dff_mode || !clk_str.empty()) { abc9_module(design, mod, script_file, exe_file, cleanup, lut_costs, dff_mode, clk_str, keepff, delay_target, lutin_shared, fast_mode, show_tempdir, - box_file, lut_file, wire_delay, box_lookup, scc_break_inputs); + box_file, lut_file, wire_delay, box_lookup); continue; } @@ -1290,7 +1264,7 @@ struct Abc9Pass : public Pass { en_sig = assign_map(std::get<3>(it.first)); abc9_module(design, mod, script_file, exe_file, cleanup, lut_costs, !clk_sig.empty(), "$", keepff, delay_target, lutin_shared, fast_mode, show_tempdir, - box_file, lut_file, wire_delay, box_lookup, scc_break_inputs); + box_file, lut_file, wire_delay, box_lookup); assign_map.set(mod); } } diff --git a/passes/techmap/techmap.cc b/passes/techmap/techmap.cc index c4496f76f..5ce1bf7d6 100644 --- a/passes/techmap/techmap.cc +++ b/passes/techmap/techmap.cc @@ -205,20 +205,38 @@ struct TechmapWorker } std::map<RTLIL::IdString, RTLIL::IdString> positional_ports; + dict<Wire*, IdString> temp_renamed_wires; for (auto &it : tpl->wires_) { if (it.second->port_id > 0) positional_ports[stringf("$%d", it.second->port_id)] = it.first; IdString w_name = it.second->name; apply_prefix(cell->name, w_name); - RTLIL::Wire *w = module->addWire(w_name, it.second); - w->port_input = false; - w->port_output = false; - w->port_id = 0; - if (it.second->get_bool_attribute(ID(_techmap_special_))) - w->attributes.clear(); - if (w->attributes.count(ID(src))) - w->add_strpool_attribute(ID(src), extra_src_attrs); + RTLIL::Wire *w = module->wire(w_name); + if (w != nullptr) { + if (!flatten_mode || !w->get_bool_attribute(ID(hierconn))) { + temp_renamed_wires[w] = w->name; + module->rename(w, NEW_ID); + w = nullptr; + } else { + w->attributes.erase(ID(hierconn)); + if (GetSize(w) < GetSize(it.second)) { + log_warning("Widening signal %s.%s to match size of %s.%s (via %s.%s).\n", log_id(module), log_id(w), + log_id(tpl), log_id(it.second), log_id(module), log_id(cell)); + w->width = GetSize(it.second); + } + } + } + if (w == nullptr) { + w = module->addWire(w_name, it.second); + w->port_input = false; + w->port_output = false; + w->port_id = 0; + if (it.second->get_bool_attribute(ID(_techmap_special_))) + w->attributes.clear(); + if (w->attributes.count(ID(src))) + w->add_strpool_attribute(ID(src), extra_src_attrs); + } design->select(module, w); } @@ -380,6 +398,16 @@ struct TechmapWorker } module->remove(cell); + + for (auto &it : temp_renamed_wires) + { + Wire *w = it.first; + IdString name = it.second; + IdString altname = module->uniquify(name); + Wire *other_w = module->wire(name); + module->rename(other_w, altname); + module->rename(w, name); + } } bool techmap_module(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Design *map, std::set<RTLIL::Cell*> &handled_cells, diff --git a/passes/tests/test_autotb.cc b/passes/tests/test_autotb.cc index bfb1d6642..7f11e54f3 100644 --- a/passes/tests/test_autotb.cc +++ b/passes/tests/test_autotb.cc @@ -348,6 +348,11 @@ struct TestAutotbBackend : public Backend { log(" -n <int>\n"); log(" number of iterations the test bench should run (default = 1000)\n"); log("\n"); + log(" -seed <int>\n"); + log(" seed used for pseudo-random number generation (default = 0).\n"); + log(" a value of 0 will cause an arbitrary seed to be chosen, based on\n"); + log(" the current system time.\n"); + log("\n"); } void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { diff --git a/techlibs/common/Makefile.inc b/techlibs/common/Makefile.inc index 0e05620bc..6c0a4fe66 100644 --- a/techlibs/common/Makefile.inc +++ b/techlibs/common/Makefile.inc @@ -28,3 +28,5 @@ $(eval $(call add_share_file,share,techlibs/common/dff2ff.v)) $(eval $(call add_share_file,share,techlibs/common/gate2lut.v)) $(eval $(call add_share_file,share,techlibs/common/cmp2lut.v)) $(eval $(call add_share_file,share,techlibs/common/cells.lib)) +$(eval $(call add_share_file,share,techlibs/common/mul2dsp.v)) +$(eval $(call add_share_file,share,techlibs/common/dummy.box)) diff --git a/techlibs/common/dummy.box b/techlibs/common/dummy.box new file mode 100644 index 000000000..0c18070a0 --- /dev/null +++ b/techlibs/common/dummy.box @@ -0,0 +1 @@ +(dummy) 1 0 0 0 diff --git a/techlibs/common/mul2dsp.v b/techlibs/common/mul2dsp.v new file mode 100644 index 000000000..75b1242a2 --- /dev/null +++ b/techlibs/common/mul2dsp.v @@ -0,0 +1,298 @@ +/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * 2019 Eddie Hung <eddie@fpgeh.com>
+ * 2019 David Shah <dave@ds0.me>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * ---
+ *
+ * Tech-mapping rules for decomposing arbitrarily-sized $mul cells
+ * into an equivalent collection of smaller `DSP_NAME cells (with the
+ * same interface as $mul) no larger than `DSP_[AB]_MAXWIDTH, attached
+ * to $shl and $add cells.
+ *
+ */
+
+`ifndef DSP_A_MAXWIDTH
+$fatal(1, "Macro DSP_A_MAXWIDTH must be defined");
+`endif
+`ifndef DSP_B_MAXWIDTH
+$fatal(1, "Macro DSP_B_MAXWIDTH must be defined");
+`endif
+`ifndef DSP_B_MAXWIDTH
+$fatal(1, "Macro DSP_B_MAXWIDTH must be defined");
+`endif
+`ifndef DSP_A_MAXWIDTH_PARTIAL
+`define DSP_A_MAXWIDTH_PARTIAL `DSP_A_MAXWIDTH
+`endif
+`ifndef DSP_B_MAXWIDTH_PARTIAL
+`define DSP_B_MAXWIDTH_PARTIAL `DSP_B_MAXWIDTH
+`endif
+
+`ifndef DSP_NAME
+$fatal(1, "Macro DSP_NAME must be defined");
+`endif
+
+`define MAX(a,b) (a > b ? a : b)
+`define MIN(a,b) (a < b ? a : b)
+
+(* techmap_celltype = "$mul $__mul" *)
+module _80_mul (A, B, Y);
+ parameter A_SIGNED = 0;
+ parameter B_SIGNED = 0;
+ parameter A_WIDTH = 1;
+ parameter B_WIDTH = 1;
+ parameter Y_WIDTH = 1;
+
+ input [A_WIDTH-1:0] A;
+ input [B_WIDTH-1:0] B;
+ output [Y_WIDTH-1:0] Y;
+
+ parameter _TECHMAP_CELLTYPE_ = "";
+
+ generate
+ if (0) begin end
+`ifdef DSP_A_MINWIDTH
+ else if (A_WIDTH < `DSP_A_MINWIDTH)
+ wire _TECHMAP_FAIL_ = 1;
+`endif
+`ifdef DSP_B_MINWIDTH
+ else if (B_WIDTH < `DSP_B_MINWIDTH)
+ wire _TECHMAP_FAIL_ = 1;
+`endif
+`ifdef DSP_Y_MINWIDTH
+ else if (Y_WIDTH < `DSP_Y_MINWIDTH)
+ wire _TECHMAP_FAIL_ = 1;
+`endif
+ else if (_TECHMAP_CELLTYPE_ == "$mul" && A_SIGNED != B_SIGNED)
+ wire _TECHMAP_FAIL_ = 1;
+`ifdef DSP_SIGNEDONLY
+ else if (_TECHMAP_CELLTYPE_ == "$mul" && !A_SIGNED)
+ \$mul #(
+ .A_SIGNED(1),
+ .B_SIGNED(1),
+ .A_WIDTH(A_WIDTH + 1),
+ .B_WIDTH(B_WIDTH + 1),
+ .Y_WIDTH(Y_WIDTH)
+ ) _TECHMAP_REPLACE_ (
+ .A({1'b0, A}),
+ .B({1'b0, B}),
+ .Y(Y)
+ );
+`endif
+ else if (_TECHMAP_CELLTYPE_ == "$mul" && A_WIDTH < B_WIDTH)
+ \$mul #(
+ .A_SIGNED(B_SIGNED),
+ .B_SIGNED(A_SIGNED),
+ .A_WIDTH(B_WIDTH),
+ .B_WIDTH(A_WIDTH),
+ .Y_WIDTH(Y_WIDTH)
+ ) _TECHMAP_REPLACE_ (
+ .A(B),
+ .B(A),
+ .Y(Y)
+ );
+ else begin
+ wire [1023:0] _TECHMAP_DO_ = "proc; clean";
+
+`ifdef DSP_SIGNEDONLY
+ localparam sign_headroom = 1;
+`else
+ localparam sign_headroom = 0;
+`endif
+
+ genvar i;
+ if (A_WIDTH > `DSP_A_MAXWIDTH) begin
+ localparam n = (A_WIDTH-`DSP_A_MAXWIDTH+`DSP_A_MAXWIDTH_PARTIAL-sign_headroom-1) / (`DSP_A_MAXWIDTH_PARTIAL-sign_headroom);
+ localparam partial_Y_WIDTH = `MIN(Y_WIDTH, B_WIDTH+`DSP_A_MAXWIDTH_PARTIAL);
+ localparam last_A_WIDTH = A_WIDTH-n*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom);
+ localparam last_Y_WIDTH = B_WIDTH+last_A_WIDTH;
+ if (A_SIGNED && B_SIGNED) begin
+ wire signed [partial_Y_WIDTH-1:0] partial [n-1:0];
+ wire signed [last_Y_WIDTH-1:0] last_partial;
+ wire signed [Y_WIDTH-1:0] partial_sum [n:0];
+ end
+ else begin
+ wire [partial_Y_WIDTH-1:0] partial [n-1:0];
+ wire [last_Y_WIDTH-1:0] last_partial;
+ wire [Y_WIDTH-1:0] partial_sum [n:0];
+ end
+
+ for (i = 0; i < n; i=i+1) begin:slice
+ \$__mul #(
+ .A_SIGNED(sign_headroom),
+ .B_SIGNED(B_SIGNED),
+ .A_WIDTH(`DSP_A_MAXWIDTH_PARTIAL),
+ .B_WIDTH(B_WIDTH),
+ .Y_WIDTH(partial_Y_WIDTH)
+ ) mul_slice (
+ .A({{sign_headroom{1'b0}}, A[i*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom) +: `DSP_A_MAXWIDTH_PARTIAL-sign_headroom]}),
+ .B(B),
+ .Y(partial[i])
+ );
+ // TODO: Currently a 'cascade' approach to summing the partial
+ // products is taken here, but a more efficient 'binary
+ // reduction' approach also exists...
+ if (i == 0)
+ assign partial_sum[i] = partial[i];
+ else
+ assign partial_sum[i] = (partial[i] << i*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom)) + partial_sum[i-1];
+ end
+
+ \$__mul #(
+ .A_SIGNED(A_SIGNED),
+ .B_SIGNED(B_SIGNED),
+ .A_WIDTH(last_A_WIDTH),
+ .B_WIDTH(B_WIDTH),
+ .Y_WIDTH(last_Y_WIDTH)
+ ) mul_slice_last (
+ .A(A[A_WIDTH-1 -: last_A_WIDTH]),
+ .B(B),
+ .Y(last_partial)
+ );
+ assign partial_sum[n] = (last_partial << n*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom)) + partial_sum[n-1];
+ assign Y = partial_sum[n];
+ end
+ else if (B_WIDTH > `DSP_B_MAXWIDTH) begin
+ localparam n = (B_WIDTH-`DSP_B_MAXWIDTH+`DSP_B_MAXWIDTH_PARTIAL-sign_headroom-1) / (`DSP_B_MAXWIDTH_PARTIAL-sign_headroom);
+ localparam partial_Y_WIDTH = `MIN(Y_WIDTH, A_WIDTH+`DSP_B_MAXWIDTH_PARTIAL);
+ localparam last_B_WIDTH = B_WIDTH-n*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom);
+ localparam last_Y_WIDTH = A_WIDTH+last_B_WIDTH;
+ if (A_SIGNED && B_SIGNED) begin
+ wire signed [partial_Y_WIDTH-1:0] partial [n-1:0];
+ wire signed [last_Y_WIDTH-1:0] last_partial;
+ wire signed [Y_WIDTH-1:0] partial_sum [n:0];
+ end
+ else begin
+ wire [partial_Y_WIDTH-1:0] partial [n-1:0];
+ wire [last_Y_WIDTH-1:0] last_partial;
+ wire [Y_WIDTH-1:0] partial_sum [n:0];
+ end
+
+ for (i = 0; i < n; i=i+1) begin:slice
+ \$__mul #(
+ .A_SIGNED(A_SIGNED),
+ .B_SIGNED(sign_headroom),
+ .A_WIDTH(A_WIDTH),
+ .B_WIDTH(`DSP_B_MAXWIDTH_PARTIAL),
+ .Y_WIDTH(partial_Y_WIDTH)
+ ) mul (
+ .A(A),
+ .B({{sign_headroom{1'b0}}, B[i*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom) +: `DSP_B_MAXWIDTH_PARTIAL-sign_headroom]}),
+ .Y(partial[i])
+ );
+ // TODO: Currently a 'cascade' approach to summing the partial
+ // products is taken here, but a more efficient 'binary
+ // reduction' approach also exists...
+ if (i == 0)
+ assign partial_sum[i] = partial[i];
+ else
+ assign partial_sum[i] = (partial[i] << i*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom)) + partial_sum[i-1];
+ end
+
+ \$__mul #(
+ .A_SIGNED(A_SIGNED),
+ .B_SIGNED(B_SIGNED),
+ .A_WIDTH(A_WIDTH),
+ .B_WIDTH(last_B_WIDTH),
+ .Y_WIDTH(last_Y_WIDTH)
+ ) mul_last (
+ .A(A),
+ .B(B[B_WIDTH-1 -: last_B_WIDTH]),
+ .Y(last_partial)
+ );
+ assign partial_sum[n] = (last_partial << n*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom)) + partial_sum[n-1];
+ assign Y = partial_sum[n];
+ end
+ else begin
+ if (A_SIGNED)
+ wire signed [`DSP_A_MAXWIDTH-1:0] Aext = $signed(A);
+ else
+ wire [`DSP_A_MAXWIDTH-1:0] Aext = A;
+ if (B_SIGNED)
+ wire signed [`DSP_B_MAXWIDTH-1:0] Bext = $signed(B);
+ else
+ wire [`DSP_B_MAXWIDTH-1:0] Bext = B;
+
+ `DSP_NAME #(
+ .A_SIGNED(A_SIGNED),
+ .B_SIGNED(B_SIGNED),
+ .A_WIDTH(`DSP_A_MAXWIDTH),
+ .B_WIDTH(`DSP_B_MAXWIDTH),
+ .Y_WIDTH(`MIN(Y_WIDTH,`DSP_A_MAXWIDTH+`DSP_B_MAXWIDTH)),
+ ) _TECHMAP_REPLACE_ (
+ .A(Aext),
+ .B(Bext),
+ .Y(Y)
+ );
+ end
+ end
+ endgenerate
+endmodule
+
+(* techmap_celltype = "$mul $__mul" *)
+module _90_soft_mul (A, B, Y);
+ parameter A_SIGNED = 0;
+ parameter B_SIGNED = 0;
+ parameter A_WIDTH = 1;
+ parameter B_WIDTH = 1;
+ parameter Y_WIDTH = 1;
+
+ input [A_WIDTH-1:0] A;
+ input [B_WIDTH-1:0] B;
+ output [Y_WIDTH-1:0] Y;
+
+ // Indirection necessary since mapping
+ // back to $mul will cause recursion
+ generate
+ if (A_SIGNED && !B_SIGNED)
+ \$__soft_mul #(
+ .A_SIGNED(A_SIGNED),
+ .B_SIGNED(1),
+ .A_WIDTH(A_WIDTH),
+ .B_WIDTH(B_WIDTH+1),
+ .Y_WIDTH(Y_WIDTH)
+ ) _TECHMAP_REPLACE_ (
+ .A(A),
+ .B({1'b0,B}),
+ .Y(Y)
+ );
+ else if (!A_SIGNED && B_SIGNED)
+ \$__soft_mul #(
+ .A_SIGNED(1),
+ .B_SIGNED(B_SIGNED),
+ .A_WIDTH(A_WIDTH+1),
+ .B_WIDTH(B_WIDTH),
+ .Y_WIDTH(Y_WIDTH)
+ ) _TECHMAP_REPLACE_ (
+ .A({1'b0,A}),
+ .B(B),
+ .Y(Y)
+ );
+ else
+ \$__soft_mul #(
+ .A_SIGNED(A_SIGNED),
+ .B_SIGNED(B_SIGNED),
+ .A_WIDTH(A_WIDTH),
+ .B_WIDTH(B_WIDTH),
+ .Y_WIDTH(Y_WIDTH)
+ ) _TECHMAP_REPLACE_ (
+ .A(A),
+ .B(B),
+ .Y(Y)
+ );
+ endgenerate
+endmodule
diff --git a/techlibs/ecp5/Makefile.inc b/techlibs/ecp5/Makefile.inc index 2143acae6..80eee5004 100644 --- a/techlibs/ecp5/Makefile.inc +++ b/techlibs/ecp5/Makefile.inc @@ -13,7 +13,11 @@ $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/brams_map.v)) $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/bram.txt)) $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/arith_map.v)) $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/latches_map.v)) +$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/dsp_map.v)) +$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc_map.v)) +$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc_unmap.v)) +$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc_model.v)) $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc_5g.box)) $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc_5g.lut)) $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc_5g_nowide.lut)) diff --git a/techlibs/ecp5/abc_5g.box b/techlibs/ecp5/abc_5g.box index c757d137d..a336b4a85 100644 --- a/techlibs/ecp5/abc_5g.box +++ b/techlibs/ecp5/abc_5g.box @@ -15,16 +15,16 @@ CCU2C 1 1 9 3 630 379 630 379 526 275 392 141 273 516 516 516 516 412 412 278 278 43 -# Box 2 : TRELLIS_DPR16X4 (16x4 dist ram) +# Box 2 : TRELLIS_DPR16X4_COMB (16x4 dist ram) # Outputs: DO0, DO1, DO2, DO3 -# name ID w/b ins outs -TRELLIS_DPR16X4 2 0 14 4 - -#DI0 DI1 DI2 DI3 RAD0 RAD1 RAD2 RAD3 WAD0 WAD1 WAD2 WAD3 WCK WRE -- - - - 141 379 275 379 - - - - - - -- - - - 141 379 275 379 - - - - - - -- - - - 141 379 275 379 - - - - - - -- - - - 141 379 275 379 - - - - - - +# name ID w/b ins outs +$__ABC_DPR16X4_COMB 2 0 8 4 + +#A0 A1 A2 A3 RAD0 RAD1 RAD2 RAD3 +0 0 0 0 141 379 275 379 +0 0 0 0 141 379 275 379 +0 0 0 0 141 379 275 379 +0 0 0 0 141 379 275 379 # Box 3 : PFUMX (MUX2) # Outputs: Z diff --git a/techlibs/ecp5/abc_map.v b/techlibs/ecp5/abc_map.v new file mode 100644 index 000000000..ffd25f06d --- /dev/null +++ b/techlibs/ecp5/abc_map.v @@ -0,0 +1,24 @@ +// --------------------------------------- + +module TRELLIS_DPR16X4 ( + input [3:0] DI, + input [3:0] WAD, + input WRE, + input WCK, + input [3:0] RAD, + output [3:0] DO +); + parameter WCKMUX = "WCK"; + parameter WREMUX = "WRE"; + parameter [63:0] INITVAL = 64'h0000000000000000; + wire [3:0] \$DO ; + + TRELLIS_DPR16X4 #( + .WCKMUX(WCKMUX), .WREMUX(WREMUX), .INITVAL(INITVAL) + ) _TECHMAP_REPLACE_ ( + .DI(DI), .WAD(WAD), .WRE(WRE), .WCK(WCK), + .RAD(RAD), .DO(\$DO ) + ); + + \$__ABC_DPR16X4_COMB do (.A(\$DO ), .S(RAD), .Y(DO)); +endmodule diff --git a/techlibs/ecp5/abc_model.v b/techlibs/ecp5/abc_model.v new file mode 100644 index 000000000..56a733b75 --- /dev/null +++ b/techlibs/ecp5/abc_model.v @@ -0,0 +1,5 @@ +// --------------------------------------- + +(* abc_box_id=2 *) +module \$__ABC_DPR16X4_COMB (input [3:0] A, S, output [3:0] Y); +endmodule diff --git a/techlibs/ecp5/abc_unmap.v b/techlibs/ecp5/abc_unmap.v new file mode 100644 index 000000000..d43cdd93f --- /dev/null +++ b/techlibs/ecp5/abc_unmap.v @@ -0,0 +1,5 @@ +// --------------------------------------- + +module \$__ABC_DPR16X4_COMB (input [3:0] A, S, output [3:0] Y); + assign Y = A; +endmodule diff --git a/techlibs/ecp5/cells_sim.v b/techlibs/ecp5/cells_sim.v index 5bdb8395e..db77dc127 100644 --- a/techlibs/ecp5/cells_sim.v +++ b/techlibs/ecp5/cells_sim.v @@ -109,16 +109,13 @@ module PFUMX (input ALUT, BLUT, C0, output Z); endmodule // --------------------------------------- -//(* abc_box_id=2 *) module TRELLIS_DPR16X4 ( - (* abc_scc_break *) input [3:0] DI, - (* abc_scc_break *) input [3:0] WAD, - (* abc_scc_break *) input WRE, input WCK, input [3:0] RAD, + /* (* abc_arrival=<TODO> *) */ output [3:0] DO ); parameter WCKMUX = "WCK"; diff --git a/techlibs/ecp5/dsp_map.v b/techlibs/ecp5/dsp_map.v new file mode 100644 index 000000000..cb95ddb1c --- /dev/null +++ b/techlibs/ecp5/dsp_map.v @@ -0,0 +1,17 @@ +module \$__MUL18X18 (input [17:0] A, input [17:0] B, output [35:0] Y); + + parameter A_WIDTH = 18; + parameter B_WIDTH = 18; + parameter Y_WIDTH = 36; + parameter A_SIGNED = 0; + parameter B_SIGNED = 0; + + MULT18X18D _TECHMAP_REPLACE_ ( + .A0(A[0]), .A1(A[1]), .A2(A[2]), .A3(A[3]), .A4(A[4]), .A5(A[5]), .A6(A[6]), .A7(A[7]), .A8(A[8]), .A9(A[9]), .A10(A[10]), .A11(A[11]), .A12(A[12]), .A13(A[13]), .A14(A[14]), .A15(A[15]), .A16(A[16]), .A17(A[17]), + .B0(B[0]), .B1(B[1]), .B2(B[2]), .B3(B[3]), .B4(B[4]), .B5(B[5]), .B6(B[6]), .B7(B[7]), .B8(B[8]), .B9(B[9]), .B10(B[10]), .B11(B[11]), .B12(B[12]), .B13(B[13]), .B14(B[14]), .B15(B[15]), .B16(B[16]), .B17(B[17]), + .C17(1'b0), .C16(1'b0), .C15(1'b0), .C14(1'b0), .C13(1'b0), .C12(1'b0), .C11(1'b0), .C10(1'b0), .C9(1'b0), .C8(1'b0), .C7(1'b0), .C6(1'b0), .C5(1'b0), .C4(1'b0), .C3(1'b0), .C2(1'b0), .C1(1'b0), .C0(1'b0), + .SIGNEDA(A_SIGNED), .SIGNEDB(B_SIGNED), .SOURCEA(1'b0), .SOURCEB(1'b0), + + .P0(Y[0]), .P1(Y[1]), .P2(Y[2]), .P3(Y[3]), .P4(Y[4]), .P5(Y[5]), .P6(Y[6]), .P7(Y[7]), .P8(Y[8]), .P9(Y[9]), .P10(Y[10]), .P11(Y[11]), .P12(Y[12]), .P13(Y[13]), .P14(Y[14]), .P15(Y[15]), .P16(Y[16]), .P17(Y[17]), .P18(Y[18]), .P19(Y[19]), .P20(Y[20]), .P21(Y[21]), .P22(Y[22]), .P23(Y[23]), .P24(Y[24]), .P25(Y[25]), .P26(Y[26]), .P27(Y[27]), .P28(Y[28]), .P29(Y[29]), .P30(Y[30]), .P31(Y[31]), .P32(Y[32]), .P33(Y[33]), .P34(Y[34]), .P35(Y[35]) + ); +endmodule diff --git a/techlibs/ecp5/synth_ecp5.cc b/techlibs/ecp5/synth_ecp5.cc index a8075e86e..0a3dcc62c 100644 --- a/techlibs/ecp5/synth_ecp5.cc +++ b/techlibs/ecp5/synth_ecp5.cc @@ -89,6 +89,9 @@ struct SynthEcp5Pass : public ScriptPass log(" generate an output netlist (and BLIF file) suitable for VPR\n"); log(" (this feature is experimental and incomplete)\n"); log("\n"); + log(" -nodsp\n"); + log(" do not map multipliers to MULT18X18D\n"); + log("\n"); log("\n"); log("The following commands are executed by this synthesis command:\n"); help_script(); @@ -96,7 +99,7 @@ struct SynthEcp5Pass : public ScriptPass } string top_opt, blif_file, edif_file, json_file; - bool noccu2, nodffe, nobram, nolutram, nowidelut, flatten, retime, abc2, abc9, vpr; + bool noccu2, nodffe, nobram, nolutram, nowidelut, flatten, retime, abc2, abc9, nodsp, vpr; void clear_flags() YS_OVERRIDE { @@ -114,6 +117,7 @@ struct SynthEcp5Pass : public ScriptPass abc2 = false; vpr = false; abc9 = false; + nodsp = false; } void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE @@ -192,6 +196,10 @@ struct SynthEcp5Pass : public ScriptPass abc9 = true; continue; } + if (args[argidx] == "-nodsp") { + nodsp = true; + continue; + } break; } extra_args(args, argidx, design); @@ -228,7 +236,29 @@ struct SynthEcp5Pass : public ScriptPass if (check_label("coarse")) { - run("synth -run coarse"); + run("opt_expr"); + run("opt_clean"); + run("check"); + run("opt"); + run("wreduce"); + run("peepopt"); + run("opt_clean"); + run("share"); + run("techmap -map +/cmp2lut.v -D LUT_WIDTH=4"); + run("opt_expr"); + run("opt_clean"); + if (!nodsp) { + run("techmap -map +/mul2dsp.v -D DSP_A_MAXWIDTH=18 -D DSP_B_MAXWIDTH=18 -D DSP_A_MINWIDTH=2 -D DSP_B_MINWIDTH=2 -D DSP_NAME=$__MUL18X18", "(unless -nodsp)"); + run("clean", "(unless -nodsp)"); + run("techmap -map +/ecp5/dsp_map.v", "(unless -nodsp)"); + run("chtype -set $mul t:$__soft_mul", "(unless -nodsp)"); + } + run("alumacc"); + run("opt"); + run("fsm"); + run("opt -fast"); + run("memory -nomap"); + run("opt_clean"); } if (!nobram && check_label("map_bram", "(skip if -nobram)")) @@ -280,12 +310,17 @@ struct SynthEcp5Pass : public ScriptPass if (abc2 || help_mode) { run("abc", " (only if -abc2)"); } - run("techmap -map +/ecp5/latches_map.v"); + std::string techmap_args = "-map +/ecp5/latches_map.v"; + if (abc9) + techmap_args += " -map +/ecp5/abc_map.v -max_iter 1"; + run("techmap " + techmap_args); + if (abc9) { if (nowidelut) run("abc9 -lut +/ecp5/abc_5g_nowide.lut -box +/ecp5/abc_5g.box -W 200"); else run("abc9 -lut +/ecp5/abc_5g.lut -box +/ecp5/abc_5g.box -W 200"); + run("techmap -map +/ecp5/abc_unmap.v"); } else { if (nowidelut) run("abc -lut 4 -dress"); diff --git a/techlibs/ice40/Makefile.inc b/techlibs/ice40/Makefile.inc index 76a89b107..92a9956ea 100644 --- a/techlibs/ice40/Makefile.inc +++ b/techlibs/ice40/Makefile.inc @@ -27,6 +27,7 @@ $(eval $(call add_share_file,share/ice40,techlibs/ice40/cells_sim.v)) $(eval $(call add_share_file,share/ice40,techlibs/ice40/latches_map.v)) $(eval $(call add_share_file,share/ice40,techlibs/ice40/brams.txt)) $(eval $(call add_share_file,share/ice40,techlibs/ice40/brams_map.v)) +$(eval $(call add_share_file,share/ice40,techlibs/ice40/dsp_map.v)) $(eval $(call add_share_file,share/ice40,techlibs/ice40/abc_hx.box)) $(eval $(call add_share_file,share/ice40,techlibs/ice40/abc_hx.lut)) $(eval $(call add_share_file,share/ice40,techlibs/ice40/abc_lp.box)) diff --git a/techlibs/ice40/cells_sim.v b/techlibs/ice40/cells_sim.v index 2a7487f6b..8e5e0358e 100644 --- a/techlibs/ice40/cells_sim.v +++ b/techlibs/ice40/cells_sim.v @@ -2,6 +2,10 @@ `define SB_DFF_REG reg Q = 0 // `define SB_DFF_REG reg Q +`define ABC_ARRIVAL_HX(TIME) `ifdef ICE40_HX (* abc_arrival=TIME *) `endif +`define ABC_ARRIVAL_LP(TIME) `ifdef ICE40_LP (* abc_arrival=TIME *) `endif +`define ABC_ARRIVAL_U(TIME) `ifdef ICE40_U (* abc_arrival=TIME *) `endif + // SiliconBlue IO Cells module SB_IO ( @@ -169,20 +173,42 @@ module \$__ICE40_CARRY_WRAPPER ( ); endmodule +// Max delay from: https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_hx1k.txt#L90 +// https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_lp1k.txt#L90 +// https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_up5k.txt#L102 + // Positive Edge SiliconBlue FF Cells -module SB_DFF (output `SB_DFF_REG, input C, D); +module SB_DFF ( + `ABC_ARRIVAL_HX(540) + `ABC_ARRIVAL_LP(796) + `ABC_ARRIVAL_U(1391) + output `SB_DFF_REG, + input C, D +); always @(posedge C) Q <= D; endmodule -module SB_DFFE (output `SB_DFF_REG, input C, E, D); +module SB_DFFE ( + `ABC_ARRIVAL_HX(540) + `ABC_ARRIVAL_LP(796) + `ABC_ARRIVAL_U(1391) + output `SB_DFF_REG, + input C, E, D +); always @(posedge C) if (E) Q <= D; endmodule -module SB_DFFSR (output `SB_DFF_REG, input C, R, D); +module SB_DFFSR ( + `ABC_ARRIVAL_HX(540) + `ABC_ARRIVAL_LP(796) + `ABC_ARRIVAL_U(1391) + output `SB_DFF_REG, + input C, R, D +); always @(posedge C) if (R) Q <= 0; @@ -190,7 +216,13 @@ module SB_DFFSR (output `SB_DFF_REG, input C, R, D); Q <= D; endmodule -module SB_DFFR (output `SB_DFF_REG, input C, R, D); +module SB_DFFR ( + `ABC_ARRIVAL_HX(540) + `ABC_ARRIVAL_LP(796) + `ABC_ARRIVAL_U(1391) + output `SB_DFF_REG, + input C, R, D +); always @(posedge C, posedge R) if (R) Q <= 0; @@ -198,7 +230,13 @@ module SB_DFFR (output `SB_DFF_REG, input C, R, D); Q <= D; endmodule -module SB_DFFSS (output `SB_DFF_REG, input C, S, D); +module SB_DFFSS ( + `ABC_ARRIVAL_HX(540) + `ABC_ARRIVAL_LP(796) + `ABC_ARRIVAL_U(1391) + output `SB_DFF_REG, + input C, S, D +); always @(posedge C) if (S) Q <= 1; @@ -206,7 +244,13 @@ module SB_DFFSS (output `SB_DFF_REG, input C, S, D); Q <= D; endmodule -module SB_DFFS (output `SB_DFF_REG, input C, S, D); +module SB_DFFS ( + `ABC_ARRIVAL_HX(540) + `ABC_ARRIVAL_LP(796) + `ABC_ARRIVAL_U(1391) + output `SB_DFF_REG, + input C, S, D +); always @(posedge C, posedge S) if (S) Q <= 1; @@ -214,7 +258,13 @@ module SB_DFFS (output `SB_DFF_REG, input C, S, D); Q <= D; endmodule -module SB_DFFESR (output `SB_DFF_REG, input C, E, R, D); +module SB_DFFESR ( + `ABC_ARRIVAL_HX(540) + `ABC_ARRIVAL_LP(796) + `ABC_ARRIVAL_U(1391) + output `SB_DFF_REG, + input C, E, R, D +); always @(posedge C) if (E) begin if (R) @@ -224,7 +274,13 @@ module SB_DFFESR (output `SB_DFF_REG, input C, E, R, D); end endmodule -module SB_DFFER (output `SB_DFF_REG, input C, E, R, D); +module SB_DFFER ( + `ABC_ARRIVAL_HX(540) + `ABC_ARRIVAL_LP(796) + `ABC_ARRIVAL_U(1391) + output `SB_DFF_REG, + input C, E, R, D +); always @(posedge C, posedge R) if (R) Q <= 0; @@ -232,7 +288,13 @@ module SB_DFFER (output `SB_DFF_REG, input C, E, R, D); Q <= D; endmodule -module SB_DFFESS (output `SB_DFF_REG, input C, E, S, D); +module SB_DFFESS ( + `ABC_ARRIVAL_HX(540) + `ABC_ARRIVAL_LP(796) + `ABC_ARRIVAL_U(1391) + output `SB_DFF_REG, + input C, E, S, D +); always @(posedge C) if (E) begin if (S) @@ -242,7 +304,13 @@ module SB_DFFESS (output `SB_DFF_REG, input C, E, S, D); end endmodule -module SB_DFFES (output `SB_DFF_REG, input C, E, S, D); +module SB_DFFES ( + `ABC_ARRIVAL_HX(540) + `ABC_ARRIVAL_LP(796) + `ABC_ARRIVAL_U(1391) + output `SB_DFF_REG, + input C, E, S, D +); always @(posedge C, posedge S) if (S) Q <= 1; @@ -252,18 +320,36 @@ endmodule // Negative Edge SiliconBlue FF Cells -module SB_DFFN (output `SB_DFF_REG, input C, D); +module SB_DFFN ( + `ABC_ARRIVAL_HX(540) + `ABC_ARRIVAL_LP(796) + `ABC_ARRIVAL_U(1391) + output `SB_DFF_REG, + input C, D +); always @(negedge C) Q <= D; endmodule -module SB_DFFNE (output `SB_DFF_REG, input C, E, D); +module SB_DFFNE ( + `ABC_ARRIVAL_HX(540) + `ABC_ARRIVAL_LP(796) + `ABC_ARRIVAL_U(1391) + output `SB_DFF_REG, + input C, E, D +); always @(negedge C) if (E) Q <= D; endmodule -module SB_DFFNSR (output `SB_DFF_REG, input C, R, D); +module SB_DFFNSR ( + `ABC_ARRIVAL_HX(540) + `ABC_ARRIVAL_LP(796) + `ABC_ARRIVAL_U(1391) + output `SB_DFF_REG, + input C, R, D +); always @(negedge C) if (R) Q <= 0; @@ -271,7 +357,13 @@ module SB_DFFNSR (output `SB_DFF_REG, input C, R, D); Q <= D; endmodule -module SB_DFFNR (output `SB_DFF_REG, input C, R, D); +module SB_DFFNR ( + `ABC_ARRIVAL_HX(540) + `ABC_ARRIVAL_LP(796) + `ABC_ARRIVAL_U(1391) + output `SB_DFF_REG, + input C, R, D +); always @(negedge C, posedge R) if (R) Q <= 0; @@ -279,7 +371,13 @@ module SB_DFFNR (output `SB_DFF_REG, input C, R, D); Q <= D; endmodule -module SB_DFFNSS (output `SB_DFF_REG, input C, S, D); +module SB_DFFNSS ( + `ABC_ARRIVAL_HX(540) + `ABC_ARRIVAL_LP(796) + `ABC_ARRIVAL_U(1391) + output `SB_DFF_REG, + input C, S, D +); always @(negedge C) if (S) Q <= 1; @@ -287,7 +385,13 @@ module SB_DFFNSS (output `SB_DFF_REG, input C, S, D); Q <= D; endmodule -module SB_DFFNS (output `SB_DFF_REG, input C, S, D); +module SB_DFFNS ( + `ABC_ARRIVAL_HX(540) + `ABC_ARRIVAL_LP(796) + `ABC_ARRIVAL_U(1391) + output `SB_DFF_REG, + input C, S, D +); always @(negedge C, posedge S) if (S) Q <= 1; @@ -295,7 +399,13 @@ module SB_DFFNS (output `SB_DFF_REG, input C, S, D); Q <= D; endmodule -module SB_DFFNESR (output `SB_DFF_REG, input C, E, R, D); +module SB_DFFNESR ( + `ABC_ARRIVAL_HX(540) + `ABC_ARRIVAL_LP(796) + `ABC_ARRIVAL_U(1391) + output `SB_DFF_REG, + input C, E, R, D +); always @(negedge C) if (E) begin if (R) @@ -305,7 +415,13 @@ module SB_DFFNESR (output `SB_DFF_REG, input C, E, R, D); end endmodule -module SB_DFFNER (output `SB_DFF_REG, input C, E, R, D); +module SB_DFFNER ( + `ABC_ARRIVAL_HX(540) + `ABC_ARRIVAL_LP(796) + `ABC_ARRIVAL_U(1391) + output `SB_DFF_REG, + input C, E, R, D +); always @(negedge C, posedge R) if (R) Q <= 0; @@ -313,7 +429,13 @@ module SB_DFFNER (output `SB_DFF_REG, input C, E, R, D); Q <= D; endmodule -module SB_DFFNESS (output `SB_DFF_REG, input C, E, S, D); +module SB_DFFNESS ( + `ABC_ARRIVAL_HX(540) + `ABC_ARRIVAL_LP(796) + `ABC_ARRIVAL_U(1391) + output `SB_DFF_REG, + input C, E, S, D +); always @(negedge C) if (E) begin if (S) @@ -323,7 +445,13 @@ module SB_DFFNESS (output `SB_DFF_REG, input C, E, S, D); end endmodule -module SB_DFFNES (output `SB_DFF_REG, input C, E, S, D); +module SB_DFFNES ( + `ABC_ARRIVAL_HX(540) + `ABC_ARRIVAL_LP(796) + `ABC_ARRIVAL_U(1391) + output `SB_DFF_REG, + input C, E, S, D +); always @(negedge C, posedge S) if (S) Q <= 1; @@ -334,6 +462,9 @@ endmodule // SiliconBlue RAM Cells module SB_RAM40_4K ( + `ABC_ARRIVAL_HX(2146) // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_hx1k.txt#L401 + `ABC_ARRIVAL_LP(3163) // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_lp1k.txt#L401 + `ABC_ARRIVAL_U(1179) // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_up5k.txt#L13026 output [15:0] RDATA, input RCLK, RCLKE, RE, input [10:0] RADDR, @@ -502,6 +633,9 @@ module SB_RAM40_4K ( endmodule module SB_RAM40_4KNR ( + `ABC_ARRIVAL_HX(2146) // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_hx1k.txt#L401 + `ABC_ARRIVAL_LP(3163) // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_lp1k.txt#L401 + `ABC_ARRIVAL_U(1179) // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_up5k.txt#L13026 output [15:0] RDATA, input RCLKN, RCLKE, RE, input [10:0] RADDR, @@ -567,6 +701,9 @@ module SB_RAM40_4KNR ( endmodule module SB_RAM40_4KNW ( + `ABC_ARRIVAL_HX(2146) // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_hx1k.txt#L401 + `ABC_ARRIVAL_LP(3163) // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_lp1k.txt#L401 + `ABC_ARRIVAL_U(1179) // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_up5k.txt#L13026 output [15:0] RDATA, input RCLK, RCLKE, RE, input [10:0] RADDR, @@ -632,6 +769,9 @@ module SB_RAM40_4KNW ( endmodule module SB_RAM40_4KNRNW ( + `ABC_ARRIVAL_HX(2146) // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_hx1k.txt#L401 + `ABC_ARRIVAL_LP(3163) // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_lp1k.txt#L401 + `ABC_ARRIVAL_U(1179) // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_up5k.txt#L13026 output [15:0] RDATA, input RCLKN, RCLKE, RE, input [10:0] RADDR, @@ -700,7 +840,12 @@ endmodule module ICESTORM_LC ( input I0, I1, I2, I3, CIN, CLK, CEN, SR, - output LO, O, COUT + output LO, + `ABC_ARRIVAL_HX(540) + `ABC_ARRIVAL_LP(796) + `ABC_ARRIVAL_U(1391) + output O, + output COUT ); parameter [15:0] LUT_INIT = 0; @@ -1300,6 +1445,7 @@ module SB_MAC16 ( input ADDSUBTOP, ADDSUBBOT, input OHOLDTOP, OHOLDBOT, input CI, ACCUMCI, SIGNEXTIN, + //`ABC_ARRIVAL_U(1984) // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_up5k.txt#L13026 output [31:0] O, output CO, ACCUMCO, SIGNEXTOUT ); diff --git a/techlibs/ice40/dsp_map.v b/techlibs/ice40/dsp_map.v new file mode 100644 index 000000000..06fa73956 --- /dev/null +++ b/techlibs/ice40/dsp_map.v @@ -0,0 +1,34 @@ +module \$__MUL16X16 (input [15:0] A, input [15:0] B, output [31:0] Y); + parameter A_SIGNED = 0; + parameter B_SIGNED = 0; + parameter A_WIDTH = 0; + parameter B_WIDTH = 0; + parameter Y_WIDTH = 0; + + SB_MAC16 #( + .NEG_TRIGGER(1'b0), + .C_REG(1'b0), + .A_REG(1'b0), + .B_REG(1'b0), + .D_REG(1'b0), + .TOP_8x8_MULT_REG(1'b0), + .BOT_8x8_MULT_REG(1'b0), + .PIPELINE_16x16_MULT_REG1(1'b0), + .PIPELINE_16x16_MULT_REG2(1'b0), + .TOPOUTPUT_SELECT(2'b11), + .TOPADDSUB_LOWERINPUT(2'b0), + .TOPADDSUB_UPPERINPUT(1'b0), + .TOPADDSUB_CARRYSELECT(2'b0), + .BOTOUTPUT_SELECT(2'b11), + .BOTADDSUB_LOWERINPUT(2'b0), + .BOTADDSUB_UPPERINPUT(1'b0), + .BOTADDSUB_CARRYSELECT(2'b0), + .MODE_8x8(1'b0), + .A_SIGNED(A_SIGNED), + .B_SIGNED(B_SIGNED) + ) _TECHMAP_REPLACE_ ( + .A(A), + .B(B), + .O(Y), + ); +endmodule diff --git a/techlibs/ice40/synth_ice40.cc b/techlibs/ice40/synth_ice40.cc index c6de81bd9..55aa72aa7 100644 --- a/techlibs/ice40/synth_ice40.cc +++ b/techlibs/ice40/synth_ice40.cc @@ -238,7 +238,14 @@ struct SynthIce40Pass : public ScriptPass { if (check_label("begin")) { - run("read_verilog -icells -lib +/ice40/cells_sim.v"); + std::string define; + if (device_opt == "lp") + define = "-D ICE40_LP"; + else if (device_opt == "u") + define = "-D ICE40_U"; + else + define = "-D ICE40_HX"; + run("read_verilog -icells " + define + " -lib +/ice40/cells_sim.v"); run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str())); run("proc"); } @@ -265,8 +272,13 @@ struct SynthIce40Pass : public ScriptPass run("techmap -map +/cmp2lut.v -D LUT_WIDTH=4"); run("opt_expr"); run("opt_clean"); - if (help_mode || dsp) - run("ice40_dsp", "(if -dsp)"); + if (help_mode || dsp) { + run("techmap -map +/mul2dsp.v -map +/ice40/dsp_map.v -D DSP_A_MAXWIDTH=16 -D DSP_B_MAXWIDTH=16 -D DSP_A_MINWIDTH=2 -D DSP_B_MINWIDTH=2 -D DSP_Y_MINWIDTH=11 -D DSP_NAME=$__MUL16X16", "(if -dsp)"); + run("opt_expr -fine", " (if -dsp)"); + run("wreduce", " (if -dsp)"); + run("ice40_dsp", " (if -dsp)"); + run("chtype -set $mul t:$__soft_mul","(if -dsp)"); + } run("alumacc"); run("opt"); run("fsm"); diff --git a/techlibs/xilinx/Makefile.inc b/techlibs/xilinx/Makefile.inc index 2b1af289c..2cf0e8e33 100644 --- a/techlibs/xilinx/Makefile.inc +++ b/techlibs/xilinx/Makefile.inc @@ -38,7 +38,11 @@ $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/arith_map.v)) $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/ff_map.v)) $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/lut_map.v)) $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/mux_map.v)) +$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/dsp_map.v)) +$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc_map.v)) +$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc_unmap.v)) +$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc_model.v)) $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc_xc7.box)) $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc_xc7.lut)) $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc_xc7_nowide.lut)) diff --git a/techlibs/xilinx/abc_map.v b/techlibs/xilinx/abc_map.v new file mode 100644 index 000000000..0c85d6656 --- /dev/null +++ b/techlibs/xilinx/abc_map.v @@ -0,0 +1,123 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * 2019 Eddie Hung <eddie@fpgeh.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +// ============================================================================ + +module RAM32X1D ( + output DPO, SPO, + input D, + input WCLK, + input WE, + input A0, A1, A2, A3, A4, + input DPRA0, DPRA1, DPRA2, DPRA3, DPRA4 +); + parameter INIT = 32'h0; + parameter IS_WCLK_INVERTED = 1'b0; + wire \$DPO , \$SPO ; + RAM32X1D #( + .INIT(INIT), .IS_WCLK_INVERTED(IS_WCLK_INVERTED) + ) _TECHMAP_REPLACE_ ( + .DPO(\$DPO ), .SPO(\$SPO ), + .D(D), .WCLK(WCLK), .WE(WE), + .A0(A0), .A1(A1), .A2(A2), .A3(A3), .A4(A4), + .DPRA0(DPRA0), .DPRA1(DPRA1), .DPRA2(DPRA2), .DPRA3(DPRA3), .DPRA4(DPRA4) + ); + \$__ABC_LUT6 dpo (.A(\$DPO ), .S({1'b0, A0, A1, A2, A3, A4}), .Y(DPO)); + \$__ABC_LUT6 spo (.A(\$SPO ), .S({1'b0, A0, A1, A2, A3, A4}), .Y(SPO)); +endmodule + +module RAM64X1D ( + output DPO, SPO, + input D, + input WCLK, + input WE, + input A0, A1, A2, A3, A4, A5, + input DPRA0, DPRA1, DPRA2, DPRA3, DPRA4, DPRA5 +); + parameter INIT = 64'h0; + parameter IS_WCLK_INVERTED = 1'b0; + wire \$DPO , \$SPO ; + RAM64X1D #( + .INIT(INIT), .IS_WCLK_INVERTED(IS_WCLK_INVERTED) + ) _TECHMAP_REPLACE_ ( + .DPO(\$DPO ), .SPO(\$SPO ), + .D(D), .WCLK(WCLK), .WE(WE), + .A0(A0), .A1(A1), .A2(A2), .A3(A3), .A4(A4), .A5(A5), + .DPRA0(DPRA0), .DPRA1(DPRA1), .DPRA2(DPRA2), .DPRA3(DPRA3), .DPRA4(DPRA4), .DPRA5(DPRA5) + ); + \$__ABC_LUT6 dpo (.A(\$DPO ), .S({A0, A1, A2, A3, A4, A5}), .Y(DPO)); + \$__ABC_LUT6 spo (.A(\$SPO ), .S({A0, A1, A2, A3, A4, A5}), .Y(SPO)); +endmodule + +module RAM128X1D ( + output DPO, SPO, + input D, + input WCLK, + input WE, + input [6:0] A, DPRA +); + parameter INIT = 128'h0; + parameter IS_WCLK_INVERTED = 1'b0; + wire \$DPO , \$SPO ; + RAM128X1D #( + .INIT(INIT), .IS_WCLK_INVERTED(IS_WCLK_INVERTED) + ) _TECHMAP_REPLACE_ ( + .DPO(\$DPO ), .SPO(\$SPO ), + .D(D), .WCLK(WCLK), .WE(WE), + .A(A), + .DPRA(DPRA) + ); + \$__ABC_LUT7 dpo (.A(\$DPO ), .S(A), .Y(DPO)); + \$__ABC_LUT7 spo (.A(\$SPO ), .S(A), .Y(SPO)); +endmodule + +module SRL16E ( + output Q, + input A0, A1, A2, A3, CE, CLK, D +); + parameter [15:0] INIT = 16'h0000; + parameter [0:0] IS_CLK_INVERTED = 1'b0; + wire \$Q ; + SRL16E #( + .INIT(INIT), .IS_CLK_INVERTED(IS_CLK_INVERTED) + ) _TECHMAP_REPLACE_ ( + .Q(\$Q ), + .A0(A0), .A1(A1), .A2(A2), .A3(A3), .CE(CE), .CLK(CLK), .D(D) + ); + \$__ABC_LUT6 q (.A(\$Q ), .S({1'b1, A0, A1, A2, A3, 1'b1}), .Y(Q)); +endmodule + +module SRLC32E ( + output Q, + output Q31, + input [4:0] A, + input CE, CLK, D +); + parameter [31:0] INIT = 32'h00000000; + parameter [0:0] IS_CLK_INVERTED = 1'b0; + wire \$Q ; + SRLC32E #( + .INIT(INIT), .IS_CLK_INVERTED(IS_CLK_INVERTED) + ) _TECHMAP_REPLACE_ ( + .Q(\$Q ), .Q31(Q31), + .A(A), .CE(CE), .CLK(CLK), .D(D) + ); + \$__ABC_LUT6 q (.A(\$Q ), .S({1'b1, A}), .Y(Q)); +endmodule diff --git a/techlibs/xilinx/abc_model.v b/techlibs/xilinx/abc_model.v new file mode 100644 index 000000000..655b993f6 --- /dev/null +++ b/techlibs/xilinx/abc_model.v @@ -0,0 +1,34 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * 2019 Eddie Hung <eddie@fpgeh.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +// ============================================================================ + +(* abc_box_id = 3, lib_whitebox *) +module \$__XILINX_MUXF78 (output O, input I0, I1, I2, I3, S0, S1); + assign O = S1 ? (S0 ? I3 : I2) + : (S0 ? I1 : I0); +endmodule + +(* abc_box_id=2000 *) +module \$__ABC_LUT6 (input A, input [5:0] S, output Y); +endmodule +(* abc_box_id=2001 *) +module \$__ABC_LUT7 (input A, input [6:0] S, output Y); +endmodule diff --git a/techlibs/xilinx/abc_unmap.v b/techlibs/xilinx/abc_unmap.v new file mode 100644 index 000000000..f101a22d0 --- /dev/null +++ b/techlibs/xilinx/abc_unmap.v @@ -0,0 +1,28 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * 2019 Eddie Hung <eddie@fpgeh.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +// ============================================================================ + +module \$__ABC_LUT6 (input A, input [5:0] S, output Y); + assign Y = A; +endmodule +module \$__ABC_LUT7 (input A, input [6:0] S, output Y); + assign Y = A; +endmodule diff --git a/techlibs/xilinx/abc_xc7.box b/techlibs/xilinx/abc_xc7.box index 3789ff350..20da3b8a0 100644 --- a/techlibs/xilinx/abc_xc7.box +++ b/techlibs/xilinx/abc_xc7.box @@ -1,4 +1,5 @@ # Max delays from https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLL_L.sdf +# https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/slicel.sdf # NB: Inputs/Outputs must be ordered alphabetically # (with exceptions for carry in/out) @@ -14,6 +15,10 @@ F7MUX 1 1 3 1 MUXF8 2 1 3 1 104 94 273 +# Box containing MUXF7.[AB] + MUXF8, +# Necessary to make these an atomic unit so that +# ABC cannot optimise just one of the MUXF7 away +# and expect to save on its delay # Inputs: I0 I1 I2 I3 S0 S1 # Outputs: O $__MUXF78 3 1 6 1 @@ -37,22 +42,20 @@ CARRY4 4 1 10 8 580 526 507 398 385 508 528 378 380 114 # SLICEM/A6LUT -# Inputs: A0 A1 A2 A3 A4 D DPRA0 DPRA1 DPRA2 DPRA3 DPRA4 WCLK WE +# Box to emulate comb/seq behaviour of RAMD{32,64} and SRL{16,32} +# Necessary since RAMD* and SRL* have both combinatorial (i.e. +# same-cycle read operation) and sequential (write operation +# is only committed on the next clock edge). +# To model the combinatorial path, such cells have to be split +# into comb and seq parts, with this box modelling only the former. +# Inputs: A S0 S1 S2 S3 S4 S5 +# Outputs: Y +$__ABC_LUT6 2000 0 7 1 +0 642 631 472 407 238 127 + +# SLICEM/A6LUT + F7BMUX +# Box to emulate comb/seq behaviour of RAMD128 +# Inputs: A S0 S1 S2 S3 S4 S5 S6 # Outputs: DPO SPO -RAM32X1D 5 0 13 2 -- - - - - - 631 472 407 238 127 - - -631 472 407 238 127 - - - - - - - - - -# SLICEM/A6LUT -# Inputs: A0 A1 A2 A3 A4 A5 D DPRA0 DPRA1 DPRA2 DPRA3 DPRA4 DPRA5 WCLK WE -# Outputs: DPO SPO -RAM64X1D 6 0 15 2 -- - - - - - - 642 631 472 407 238 127 - - -642 631 472 407 238 127 - - - - - - - - - - -# SLICEM/A6LUT + F7[AB]MUX -# Inputs: A0 A1 A2 A3 A4 A5 A6 D DPRA0 DPRA1 DPRA2 DPRA3 DPRA4 DPRA5 DPRA6 WCLK WE -# Outputs: DPO SPO -RAM128X1D 7 0 17 2 -- - - - - - - - 1009 998 839 774 605 494 450 - - -1047 1036 877 812 643 532 478 - - - - - - - - - - +$__ABC_LUT7 2001 0 8 1 +0 1047 1036 877 812 643 532 478 diff --git a/techlibs/xilinx/cells_map.v b/techlibs/xilinx/cells_map.v index b8e5bafc7..a15884ec4 100644 --- a/techlibs/xilinx/cells_map.v +++ b/techlibs/xilinx/cells_map.v @@ -331,7 +331,6 @@ module \$_MUX16_ (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, S, T, U, V, Y) endmodule `endif -`ifndef _ABC module \$__XILINX_MUXF78 (O, I0, I1, I2, I3, S0, S1); output O; input I0, I1, I2, I3, S0, S1; @@ -364,4 +363,3 @@ module \$__XILINX_MUXF78 (O, I0, I1, I2, I3, S0, S1); else MUXF8 mux8 (.I0(T0), .I1(T1), .S(S1), .O(O)); endmodule -`endif diff --git a/techlibs/xilinx/cells_sim.v b/techlibs/xilinx/cells_sim.v index e12b77c02..ed421f85e 100644 --- a/techlibs/xilinx/cells_sim.v +++ b/techlibs/xilinx/cells_sim.v @@ -184,14 +184,6 @@ module MUXF8(output O, input I0, I1, S); assign O = S ? I1 : I0; endmodule -`ifdef _ABC -(* abc_box_id = 3, lib_whitebox *) -module \$__XILINX_MUXF78 (output O, input I0, I1, I2, I3, S0, S1); - assign O = S1 ? (S0 ? I3 : I2) - : (S0 ? I1 : I0); -endmodule -`endif - module XORCY(output O, input CI, LI); assign O = CI ^ LI; endmodule @@ -236,7 +228,15 @@ endmodule `endif -module FDRE (output reg Q, (* clkbuf_sink *) input C, input CE, D, R); +// Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLL_L.sdf#L238-L250 + +module FDRE ( + (* abc_arrival=303 *) + output reg Q, + (* clkbuf_sink *) + input C, + input CE, D, R +); parameter [0:0] INIT = 1'b0; parameter [0:0] IS_C_INVERTED = 1'b0; parameter [0:0] IS_D_INVERTED = 1'b0; @@ -248,7 +248,13 @@ module FDRE (output reg Q, (* clkbuf_sink *) input C, input CE, D, R); endcase endgenerate endmodule -module FDSE (output reg Q, (* clkbuf_sink *) input C, input CE, D, S); +module FDSE ( + (* abc_arrival=303 *) + output reg Q, + (* clkbuf_sink *) + input C, + input CE, D, S +); parameter [0:0] INIT = 1'b1; parameter [0:0] IS_C_INVERTED = 1'b0; parameter [0:0] IS_D_INVERTED = 1'b0; @@ -260,7 +266,13 @@ module FDSE (output reg Q, (* clkbuf_sink *) input C, input CE, D, S); endcase endgenerate endmodule -module FDCE (output reg Q, (* clkbuf_sink *) input C, input CE, D, CLR); +module FDCE ( + (* abc_arrival=303 *) + output reg Q, + (* clkbuf_sink *) + input C, + input CE, D, CLR +); parameter [0:0] INIT = 1'b0; parameter [0:0] IS_C_INVERTED = 1'b0; parameter [0:0] IS_D_INVERTED = 1'b0; @@ -274,7 +286,13 @@ module FDCE (output reg Q, (* clkbuf_sink *) input C, input CE, D, CLR); endcase endgenerate endmodule -module FDPE (output reg Q, (* clkbuf_sink *) input C, input CE, D, PRE); +module FDPE ( + (* abc_arrival=303 *) + output reg Q, + (* clkbuf_sink *) + input C, + input CE, D, PRE +); parameter [0:0] INIT = 1'b1; parameter [0:0] IS_C_INVERTED = 1'b0; parameter [0:0] IS_D_INVERTED = 1'b0; @@ -288,38 +306,61 @@ module FDPE (output reg Q, (* clkbuf_sink *) input C, input CE, D, PRE); endcase endgenerate endmodule -module FDRE_1 (output reg Q, (* clkbuf_sink *) input C, input CE, D, R); +module FDRE_1 ( + (* abc_arrival=303 *) + output reg Q, + (* clkbuf_sink *) + input C, + input CE, D, R +); parameter [0:0] INIT = 1'b0; initial Q <= INIT; always @(negedge C) if (R) Q <= 1'b0; else if(CE) Q <= D; endmodule -module FDSE_1 (output reg Q, (* clkbuf_sink *) input C, input CE, D, S); +module FDSE_1 ( + (* abc_arrival=303 *) + output reg Q, + (* clkbuf_sink *) + input C, + input CE, D, S +); parameter [0:0] INIT = 1'b1; initial Q <= INIT; always @(negedge C) if (S) Q <= 1'b1; else if(CE) Q <= D; endmodule -module FDCE_1 (output reg Q, (* clkbuf_sink *) input C, input CE, D, CLR); +module FDCE_1 ( + (* abc_arrival=303 *) + output reg Q, + (* clkbuf_sink *) + input C, + input CE, D, CLR +); parameter [0:0] INIT = 1'b0; initial Q <= INIT; always @(negedge C, posedge CLR) if (CLR) Q <= 1'b0; else if (CE) Q <= D; endmodule -module FDPE_1 (output reg Q, (* clkbuf_sink *) input C, input CE, D, PRE); +module FDPE_1 ( + (* abc_arrival=303 *) + output reg Q, + (* clkbuf_sink *) + input C, + input CE, D, PRE +); parameter [0:0] INIT = 1'b1; initial Q <= INIT; always @(negedge C, posedge PRE) if (PRE) Q <= 1'b1; else if (CE) Q <= D; endmodule -(* abc_box_id = 5 *) module RAM32X1D ( + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L957 + (* abc_arrival=1153 *) output DPO, SPO, - (* abc_scc_break *) input D, (* clkbuf_sink *) input WCLK, - (* abc_scc_break *) input WE, input A0, A1, A2, A3, A4, input DPRA0, DPRA1, DPRA2, DPRA3, DPRA4 @@ -335,14 +376,13 @@ module RAM32X1D ( always @(posedge clk) if (WE) mem[a] <= D; endmodule -(* abc_box_id = 6 *) module RAM64X1D ( + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L957 + (* abc_arrival=1153 *) output DPO, SPO, - (* abc_scc_break *) input D, (* clkbuf_sink *) input WCLK, - (* abc_scc_break *) input WE, input A0, A1, A2, A3, A4, A5, input DPRA0, DPRA1, DPRA2, DPRA3, DPRA4, DPRA5 @@ -358,14 +398,13 @@ module RAM64X1D ( always @(posedge clk) if (WE) mem[a] <= D; endmodule -(* abc_box_id = 7 *) module RAM128X1D ( - output DPO, SPO, - (* abc_scc_break *) + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L957 + (* abc_arrival=1153 *) + output DPO, SPO, input D, (* clkbuf_sink *) input WCLK, - (* abc_scc_break *) input WE, input [6:0] A, DPRA ); @@ -379,6 +418,8 @@ module RAM128X1D ( endmodule module SRL16E ( + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L904-L905 + (* abc_arrival=1472 *) output Q, input A0, A1, A2, A3, CE, (* clkbuf_sink *) @@ -423,7 +464,10 @@ module SRLC16E ( endmodule module SRLC32E ( + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L904-L905 + (* abc_arrival=1472 *) output Q, + (* abc_arrival=1114 *) output Q31, input [4:0] A, input CE, @@ -445,3 +489,412 @@ module SRLC32E ( always @(posedge CLK) if (CE) r <= { r[30:0], D }; endgenerate endmodule + +module DSP48E1 ( + output [29:0] ACOUT, + output [17:0] BCOUT, + output reg CARRYCASCOUT, + output reg [3:0] CARRYOUT, + output reg MULTSIGNOUT, + output OVERFLOW, + output reg signed [47:0] P, + output PATTERNBDETECT, + output PATTERNDETECT, + output [47:0] PCOUT, + output UNDERFLOW, + input signed [29:0] A, + input [29:0] ACIN, + input [3:0] ALUMODE, + input signed [17:0] B, + input [17:0] BCIN, + input [47:0] C, + input CARRYCASCIN, + input CARRYIN, + input [2:0] CARRYINSEL, + input CEA1, + input CEA2, + input CEAD, + input CEALUMODE, + input CEB1, + input CEB2, + input CEC, + input CECARRYIN, + input CECTRL, + input CED, + input CEINMODE, + input CEM, + input CEP, + (* clkbuf_sink *) input CLK, + input [24:0] D, + input [4:0] INMODE, + input MULTSIGNIN, + input [6:0] OPMODE, + input [47:0] PCIN, + input RSTA, + input RSTALLCARRYIN, + input RSTALUMODE, + input RSTB, + input RSTC, + input RSTCTRL, + input RSTD, + input RSTINMODE, + input RSTM, + input RSTP +); + parameter integer ACASCREG = 1; + parameter integer ADREG = 1; + parameter integer ALUMODEREG = 1; + parameter integer AREG = 1; + parameter AUTORESET_PATDET = "NO_RESET"; + parameter A_INPUT = "DIRECT"; + parameter integer BCASCREG = 1; + parameter integer BREG = 1; + parameter B_INPUT = "DIRECT"; + parameter integer CARRYINREG = 1; + parameter integer CARRYINSELREG = 1; + parameter integer CREG = 1; + parameter integer DREG = 1; + parameter integer INMODEREG = 1; + parameter integer MREG = 1; + parameter integer OPMODEREG = 1; + parameter integer PREG = 1; + parameter SEL_MASK = "MASK"; + parameter SEL_PATTERN = "PATTERN"; + parameter USE_DPORT = "FALSE"; + parameter USE_MULT = "MULTIPLY"; + parameter USE_PATTERN_DETECT = "NO_PATDET"; + parameter USE_SIMD = "ONE48"; + parameter [47:0] MASK = 48'h3FFFFFFFFFFF; + parameter [47:0] PATTERN = 48'h000000000000; + parameter [3:0] IS_ALUMODE_INVERTED = 4'b0; + parameter [0:0] IS_CARRYIN_INVERTED = 1'b0; + parameter [0:0] IS_CLK_INVERTED = 1'b0; + parameter [4:0] IS_INMODE_INVERTED = 5'b0; + parameter [6:0] IS_OPMODE_INVERTED = 7'b0; + + initial begin +`ifdef __ICARUS__ + if (AUTORESET_PATDET != "NO_RESET") $fatal(1, "Unsupported AUTORESET_PATDET value"); + //if (PREG != 0) $fatal(1, "Unsupported PREG value"); + if (SEL_MASK != "MASK") $fatal(1, "Unsupported SEL_MASK value"); + if (SEL_PATTERN != "PATTERN") $fatal(1, "Unsupported SEL_PATTERN value"); + if (USE_PATTERN_DETECT != "NO_PATDET") $fatal(1, "Unsupported USE_PATTERN_DETECT value"); + if (USE_SIMD != "ONE48" && USE_SIMD != "TWO24" && USE_SIMD != "FOUR12") $fatal(1, "Unsupported USE_SIMD value"); + if (IS_ALUMODE_INVERTED != 4'b0) $fatal(1, "Unsupported IS_ALUMODE_INVERTED value"); + if (IS_CARRYIN_INVERTED != 1'b0) $fatal(1, "Unsupported IS_CARRYIN_INVERTED value"); + if (IS_CLK_INVERTED != 1'b0) $fatal(1, "Unsupported IS_CLK_INVERTED value"); + if (IS_INMODE_INVERTED != 5'b0) $fatal(1, "Unsupported IS_INMODE_INVERTED value"); + if (IS_OPMODE_INVERTED != 7'b0) $fatal(1, "Unsupported IS_OPMODE_INVERTED value"); +`endif + end + + wire signed [29:0] A_muxed; + wire signed [17:0] B_muxed; + + generate + if (A_INPUT == "CASCADE") assign A_muxed = ACIN; + else assign A_muxed = A; + + if (B_INPUT == "CASCADE") assign B_muxed = BCIN; + else assign B_muxed = B; + endgenerate + + reg signed [29:0] Ar1 = 30'b0, Ar2 = 30'b0; + reg signed [24:0] Dr = 25'b0; + reg signed [17:0] Br1 = 18'b0, Br2 = 18'b0; + reg signed [47:0] Cr = 48'b0; + reg [4:0] INMODEr = 5'b0; + reg [6:0] OPMODEr = 7'b0; + reg [3:0] ALUMODEr = 4'b0; + reg [2:0] CARRYINSELr = 3'b0; + + generate + // Configurable A register + if (AREG == 2) begin + always @(posedge CLK) + if (RSTA) begin + Ar1 <= 30'b0; + Ar2 <= 30'b0; + end else begin + if (CEA1) Ar1 <= A_muxed; + if (CEA2) Ar2 <= Ar1; + end + end else if (AREG == 1) begin + always @(posedge CLK) + if (RSTA) begin + Ar1 <= 30'b0; + Ar2 <= 30'b0; + end else begin + if (CEA1) Ar1 <= A_muxed; + if (CEA2) Ar2 <= A_muxed; + end + end else begin + always @* Ar1 <= A_muxed; + always @* Ar2 <= A_muxed; + end + + // Configurable B register + if (BREG == 2) begin + always @(posedge CLK) + if (RSTB) begin + Br1 <= 18'b0; + Br2 <= 18'b0; + end else begin + if (CEB1) Br1 <= B_muxed; + if (CEB2) Br2 <= Br1; + end + end else if (BREG == 1) begin + always @(posedge CLK) + if (RSTB) begin + Br1 <= 18'b0; + Br2 <= 18'b0; + end else begin + if (CEB1) Br1 <= B_muxed; + if (CEB2) Br2 <= B_muxed; + end + end else begin + always @* Br1 <= B_muxed; + always @* Br2 <= B_muxed; + end + + // C and D registers + if (CREG == 1) begin always @(posedge CLK) if (RSTC) Cr <= 48'b0; else if (CEC) Cr <= C; end + else always @* Cr <= C; + + if (DREG == 1) begin always @(posedge CLK) if (RSTD) Dr <= 25'b0; else if (CED) Dr <= D; end + else always @* Dr <= D; + + // Control registers + if (INMODEREG == 1) begin always @(posedge CLK) if (RSTINMODE) INMODEr <= 5'b0; else if (CEINMODE) INMODEr <= INMODE; end + else always @* INMODEr <= INMODE; + if (OPMODEREG == 1) begin always @(posedge CLK) if (RSTCTRL) OPMODEr <= 7'b0; else if (CECTRL) OPMODEr <= OPMODE; end + else always @* OPMODEr <= OPMODE; + if (ALUMODEREG == 1) begin always @(posedge CLK) if (RSTALUMODE) ALUMODEr <= 4'b0; else if (CEALUMODE) ALUMODEr <= ALUMODE; end + else always @* ALUMODEr <= ALUMODE; + if (CARRYINSELREG == 1) begin always @(posedge CLK) if (RSTCTRL) CARRYINSELr <= 3'b0; else if (CECTRL) CARRYINSELr <= CARRYINSEL; end + else always @* CARRYINSELr <= CARRYINSEL; + endgenerate + + // A and B cascsde + generate + if (ACASCREG == 1 && AREG == 2) assign ACOUT = Ar1; + else assign ACOUT = Ar2; + if (BCASCREG == 1 && BREG == 2) assign BCOUT = Br1; + else assign BCOUT = Br2; + endgenerate + + // A/D input selection and pre-adder + wire signed [29:0] Ar12_muxed = INMODEr[0] ? Ar1 : Ar2; + wire signed [24:0] Ar12_gated = INMODEr[1] ? 25'b0 : Ar12_muxed; + wire signed [24:0] Dr_gated = INMODEr[2] ? Dr : 25'b0; + wire signed [24:0] AD_result = INMODEr[3] ? (Dr_gated - Ar12_gated) : (Dr_gated + Ar12_gated); + reg signed [24:0] ADr = 25'b0; + + generate + if (ADREG == 1) begin always @(posedge CLK) if (RSTD) ADr <= 25'b0; else if (CEAD) ADr <= AD_result; end + else always @* ADr <= AD_result; + endgenerate + + // 25x18 multiplier + wire signed [24:0] A_MULT; + wire signed [17:0] B_MULT = INMODEr[4] ? Br1 : Br2; + generate + if (USE_DPORT == "TRUE") assign A_MULT = ADr; + else assign A_MULT = Ar12_gated; + endgenerate + + wire signed [42:0] M = A_MULT * B_MULT; + wire signed [42:0] Mx = (CARRYINSEL == 3'b010) ? 43'bx : M; + reg signed [42:0] Mr = 43'b0; + + // Multiplier result register + generate + if (MREG == 1) begin always @(posedge CLK) if (RSTM) Mr <= 43'b0; else if (CEM) Mr <= Mx; end + else always @* Mr <= Mx; + endgenerate + + wire signed [42:0] Mrx = (CARRYINSELr == 3'b010) ? 43'bx : Mr; + + // X, Y and Z ALU inputs + reg signed [47:0] X, Y, Z; + + always @* begin + // X multiplexer + case (OPMODEr[1:0]) + 2'b00: X = 48'b0; + 2'b01: begin X = $signed(Mrx); +`ifdef __ICARUS__ + if (OPMODEr[3:2] != 2'b01) $fatal(1, "OPMODEr[3:2] must be 2'b01 when OPMODEr[1:0] is 2'b01"); +`endif + end + 2'b10: begin X = P; +`ifdef __ICARUS__ + if (PREG != 1) $fatal(1, "PREG must be 1 when OPMODEr[1:0] is 2'b10"); +`endif + end + 2'b11: X = $signed({Ar2, Br2}); + default: X = 48'bx; + endcase + + // Y multiplexer + case (OPMODEr[3:2]) + 2'b00: Y = 48'b0; + 2'b01: begin Y = 48'b0; // FIXME: more accurate partial product modelling? +`ifdef __ICARUS__ + if (OPMODEr[1:0] != 2'b01) $fatal(1, "OPMODEr[1:0] must be 2'b01 when OPMODEr[3:2] is 2'b01"); +`endif + end + 2'b10: Y = {48{1'b1}}; + 2'b11: Y = Cr; + default: Y = 48'bx; + endcase + + // Z multiplexer + case (OPMODEr[6:4]) + 3'b000: Z = 48'b0; + 3'b001: Z = PCIN; + 3'b010: begin Z = P; +`ifdef __ICARUS__ + if (PREG != 1) $fatal(1, "PREG must be 1 when OPMODEr[6:4] i0s 3'b010"); +`endif + end + 3'b011: Z = Cr; + 3'b100: begin Z = P; +`ifdef __ICARUS__ + if (PREG != 1) $fatal(1, "PREG must be 1 when OPMODEr[6:4] is 3'b100"); + if (OPMODEr[3:0] != 4'b1000) $fatal(1, "OPMODEr[3:0] must be 4'b1000 when OPMODEr[6:4] i0s 3'b100"); +`endif + end + 3'b101: Z = $signed(PCIN[47:17]); + 3'b110: Z = $signed(P[47:17]); + default: Z = 48'bx; + endcase + end + + // Carry in + wire A24_xnor_B17d = A_MULT[24] ~^ B_MULT[17]; + reg CARRYINr = 1'b0, A24_xnor_B17 = 1'b0; + generate + if (CARRYINREG == 1) begin always @(posedge CLK) if (RSTALLCARRYIN) CARRYINr <= 1'b0; else if (CECARRYIN) CARRYINr <= CARRYIN; end + else always @* CARRYINr = CARRYIN; + + if (MREG == 1) begin always @(posedge CLK) if (RSTALLCARRYIN) A24_xnor_B17 <= 1'b0; else if (CEM) A24_xnor_B17 <= A24_xnor_B17d; end + else always @* A24_xnor_B17 = A24_xnor_B17d; + endgenerate + + reg cin_muxed; + + always @(*) begin + case (CARRYINSELr) + 3'b000: cin_muxed = CARRYINr; + 3'b001: cin_muxed = ~PCIN[47]; + 3'b010: cin_muxed = CARRYCASCIN; + 3'b011: cin_muxed = PCIN[47]; + 3'b100: cin_muxed = CARRYCASCOUT; + 3'b101: cin_muxed = ~P[47]; + 3'b110: cin_muxed = A24_xnor_B17; + 3'b111: cin_muxed = P[47]; + default: cin_muxed = 1'bx; + endcase + end + + wire alu_cin = (ALUMODEr[3] || ALUMODEr[2]) ? 1'b0 : cin_muxed; + + // ALU core + wire [47:0] Z_muxinv = ALUMODEr[0] ? ~Z : Z; + wire [47:0] xor_xyz = X ^ Y ^ Z_muxinv; + wire [47:0] maj_xyz = (X & Y) | (X & Z_muxinv) | (Y & Z_muxinv); + + wire [47:0] xor_xyz_muxed = ALUMODEr[3] ? maj_xyz : xor_xyz; + wire [47:0] maj_xyz_gated = ALUMODEr[2] ? 48'b0 : maj_xyz; + + wire [48:0] maj_xyz_simd_gated; + wire [3:0] int_carry_in, int_carry_out, ext_carry_out; + wire [47:0] alu_sum; + assign int_carry_in[0] = 1'b0; + wire [3:0] carryout_reset; + + generate + if (USE_SIMD == "FOUR12") begin + assign maj_xyz_simd_gated = { + maj_xyz_gated[47:36], + 1'b0, maj_xyz_gated[34:24], + 1'b0, maj_xyz_gated[22:12], + 1'b0, maj_xyz_gated[10:0], + alu_cin + }; + assign int_carry_in[3:1] = 3'b000; + assign ext_carry_out = { + int_carry_out[3], + maj_xyz_gated[35] ^ int_carry_out[2], + maj_xyz_gated[23] ^ int_carry_out[1], + maj_xyz_gated[11] ^ int_carry_out[0] + }; + assign carryout_reset = 4'b0000; + end else if (USE_SIMD == "TWO24") begin + assign maj_xyz_simd_gated = { + maj_xyz_gated[47:24], + 1'b0, maj_xyz_gated[22:0], + alu_cin + }; + assign int_carry_in[3:1] = {int_carry_out[2], 1'b0, int_carry_out[0]}; + assign ext_carry_out = { + int_carry_out[3], + 1'bx, + maj_xyz_gated[23] ^ int_carry_out[1], + 1'bx + }; + assign carryout_reset = 4'b0x0x; + end else begin + assign maj_xyz_simd_gated = {maj_xyz_gated, alu_cin}; + assign int_carry_in[3:1] = int_carry_out[2:0]; + assign ext_carry_out = { + int_carry_out[3], + 3'bxxx + }; + assign carryout_reset = 4'b0xxx; + end + + genvar i; + for (i = 0; i < 4; i = i + 1) + assign {int_carry_out[i], alu_sum[i*12 +: 12]} = {1'b0, maj_xyz_simd_gated[i*12 +: ((i == 3) ? 13 : 12)]} + + xor_xyz_muxed[i*12 +: 12] + int_carry_in[i]; + endgenerate + + wire signed [47:0] Pd = ALUMODEr[1] ? ~alu_sum : alu_sum; + initial P = 48'b0; + initial CARRYOUT = carryout_reset; + initial CARRYCASCOUT = 1'b0; + initial MULTSIGNOUT = 1'b0; + wire [3:0] CARRYOUTd = (OPMODEr[3:0] == 4'b0101 || ALUMODEr[3:2] != 2'b00) ? 4'bxxxx : + ((ALUMODEr[0] & ALUMODEr[1]) ? ~ext_carry_out : ext_carry_out); + wire CARRYCASCOUTd = ext_carry_out[3]; + wire MULTSIGNOUTd = Mrx[42]; + + generate + if (PREG == 1) begin + always @(posedge CLK) + if (RSTP) begin + P <= 48'b0; + CARRYOUT <= carryout_reset; + CARRYCASCOUT <= 1'b0; + MULTSIGNOUT <= 1'b0; + end else if (CEP) begin + P <= Pd; + CARRYOUT <= CARRYOUTd; + CARRYCASCOUT <= CARRYCASCOUTd; + MULTSIGNOUT <= MULTSIGNOUTd; + end + end else begin + always @* begin + P = Pd; + CARRYOUT = CARRYOUTd; + CARRYCASCOUT = CARRYCASCOUTd; + MULTSIGNOUT = MULTSIGNOUTd; + end + end + endgenerate + + assign PCOUT = P; + +endmodule diff --git a/techlibs/xilinx/cells_xtra.v b/techlibs/xilinx/cells_xtra.v index a6669b872..b8abdda64 100644 --- a/techlibs/xilinx/cells_xtra.v +++ b/techlibs/xilinx/cells_xtra.v @@ -137,89 +137,6 @@ module DNA_PORT (...); input SHIFT; endmodule -module DSP48E1 (...); - parameter integer ACASCREG = 1; - parameter integer ADREG = 1; - parameter integer ALUMODEREG = 1; - parameter integer AREG = 1; - parameter AUTORESET_PATDET = "NO_RESET"; - parameter A_INPUT = "DIRECT"; - parameter integer BCASCREG = 1; - parameter integer BREG = 1; - parameter B_INPUT = "DIRECT"; - parameter integer CARRYINREG = 1; - parameter integer CARRYINSELREG = 1; - parameter integer CREG = 1; - parameter integer DREG = 1; - parameter integer INMODEREG = 1; - parameter integer MREG = 1; - parameter integer OPMODEREG = 1; - parameter integer PREG = 1; - parameter SEL_MASK = "MASK"; - parameter SEL_PATTERN = "PATTERN"; - parameter USE_DPORT = "FALSE"; - parameter USE_MULT = "MULTIPLY"; - parameter USE_PATTERN_DETECT = "NO_PATDET"; - parameter USE_SIMD = "ONE48"; - parameter [47:0] MASK = 48'h3FFFFFFFFFFF; - parameter [47:0] PATTERN = 48'h000000000000; - parameter [3:0] IS_ALUMODE_INVERTED = 4'b0; - parameter [0:0] IS_CARRYIN_INVERTED = 1'b0; - parameter [0:0] IS_CLK_INVERTED = 1'b0; - parameter [4:0] IS_INMODE_INVERTED = 5'b0; - parameter [6:0] IS_OPMODE_INVERTED = 7'b0; - output [29:0] ACOUT; - output [17:0] BCOUT; - output CARRYCASCOUT; - output [3:0] CARRYOUT; - output MULTSIGNOUT; - output OVERFLOW; - output [47:0] P; - output PATTERNBDETECT; - output PATTERNDETECT; - output [47:0] PCOUT; - output UNDERFLOW; - input [29:0] A; - input [29:0] ACIN; - input [3:0] ALUMODE; - input [17:0] B; - input [17:0] BCIN; - input [47:0] C; - input CARRYCASCIN; - input CARRYIN; - input [2:0] CARRYINSEL; - input CEA1; - input CEA2; - input CEAD; - input CEALUMODE; - input CEB1; - input CEB2; - input CEC; - input CECARRYIN; - input CECTRL; - input CED; - input CEINMODE; - input CEM; - input CEP; - (* clkbuf_sink *) - input CLK; - input [24:0] D; - input [4:0] INMODE; - input MULTSIGNIN; - input [6:0] OPMODE; - input [47:0] PCIN; - input RSTA; - input RSTALLCARRYIN; - input RSTALUMODE; - input RSTB; - input RSTC; - input RSTCTRL; - input RSTD; - input RSTINMODE; - input RSTM; - input RSTP; -endmodule - module EFUSE_USR (...); parameter [31:0] SIM_EFUSE_VALUE = 32'h00000000; output [31:0] EFUSEUSR; diff --git a/techlibs/xilinx/dsp_map.v b/techlibs/xilinx/dsp_map.v new file mode 100644 index 000000000..fdd55afe3 --- /dev/null +++ b/techlibs/xilinx/dsp_map.v @@ -0,0 +1,46 @@ +module \$__MUL25X18 (input [24:0] A, input [17:0] B, output [42:0] Y); + parameter A_SIGNED = 0; + parameter B_SIGNED = 0; + parameter A_WIDTH = 0; + parameter B_WIDTH = 0; + parameter Y_WIDTH = 0; + + wire [47:0] P_48; + DSP48E1 #( + // Disable all registers + .ACASCREG(0), + .ADREG(0), + .A_INPUT("DIRECT"), + .ALUMODEREG(0), + .AREG(0), + .BCASCREG(0), + .B_INPUT("DIRECT"), + .BREG(0), + .CARRYINREG(0), + .CARRYINSELREG(0), + .CREG(0), + .DREG(0), + .INMODEREG(0), + .MREG(0), + .OPMODEREG(0), + .PREG(0) + ) _TECHMAP_REPLACE_ ( + //Data path + .A({{5{A[24]}}, A}), + .B(B), + .C(48'b0), + .D(24'b0), + .P(P_48), + + .INMODE(5'b00000), + .ALUMODE(4'b0000), + .OPMODE(7'b000101), + .CARRYINSEL(3'b000), + + .ACIN(30'b0), + .BCIN(18'b0), + .PCIN(48'b0), + .CARRYIN(1'b0) + ); + assign Y = P_48; +endmodule diff --git a/techlibs/xilinx/synth_xilinx.cc b/techlibs/xilinx/synth_xilinx.cc index 3760a1129..95b2c4040 100644 --- a/techlibs/xilinx/synth_xilinx.cc +++ b/techlibs/xilinx/synth_xilinx.cc @@ -81,6 +81,8 @@ struct SynthXilinxPass : public ScriptPass log(" -nowidelut\n"); log(" do not use MUXF[78] resources to implement LUTs larger than LUT6s\n"); log("\n"); + log(" -nodsp\n"); + log(" do not use DSP48E1s to implement multipliers and associated logic\n"); log(" -iopad\n"); log(" enable I/O buffer insertion (selected automatically by -ise)\n"); log("\n"); @@ -116,7 +118,7 @@ struct SynthXilinxPass : public ScriptPass } std::string top_opt, edif_file, blif_file, family; - bool flatten, retime, vpr, ise, iopad, noiopad, noclkbuf, nobram, nolutram, nosrl, nocarry, nowidelut, abc9; + bool flatten, retime, vpr, ise, iopad, noiopad, noclkbuf, nobram, nolutram, nosrl, nocarry, nowidelut, nodsp, abc9; bool flatten_before_abc; int widemux; @@ -139,6 +141,7 @@ struct SynthXilinxPass : public ScriptPass nosrl = false; nocarry = false; nowidelut = false; + nodsp = false; abc9 = false; flatten_before_abc = false; widemux = 0; @@ -240,6 +243,10 @@ struct SynthXilinxPass : public ScriptPass abc9 = true; continue; } + if (args[argidx] == "-nodsp") { + nodsp = true; + continue; + } break; } extra_args(args, argidx, design); @@ -268,9 +275,9 @@ struct SynthXilinxPass : public ScriptPass { if (check_label("begin")) { if (vpr) - run("read_verilog -lib -icells -D _ABC -D_EXPLICIT_CARRY +/xilinx/cells_sim.v"); + run("read_verilog -lib -D_EXPLICIT_CARRY +/xilinx/cells_sim.v"); else - run("read_verilog -lib -icells -D _ABC +/xilinx/cells_sim.v"); + run("read_verilog -lib +/xilinx/cells_sim.v"); run("read_verilog -lib +/xilinx/cells_xtra.v"); @@ -285,10 +292,10 @@ struct SynthXilinxPass : public ScriptPass run(stringf("hierarchy -check %s", top_opt.c_str())); } - if (check_label("coarse")) { + if (check_label("prepare")) { run("proc"); - if (help_mode || flatten) - run("flatten", "(if -flatten)"); + if (flatten || help_mode) + run("flatten", "(with '-flatten')"); run("opt_expr"); run("opt_clean"); run("check"); @@ -312,6 +319,20 @@ struct SynthXilinxPass : public ScriptPass } run("techmap -map +/cmp2lut.v -D LUT_WIDTH=6"); + } + + if (check_label("dsp")) { + if (!nodsp || help_mode) { + // NB: Xilinx multipliers are signed only + run("techmap -map +/mul2dsp.v -map +/xilinx/dsp_map.v -D DSP_A_MAXWIDTH=25 -D DSP_A_MAXWIDTH_PARTIAL=18 -D DSP_B_MAXWIDTH=18 -D DSP_SIGNEDONLY=1 -D DSP_NAME=$__MUL25X18", "(skip if '-nodsp')"); + run("opt_expr -fine", " (skip if '-nodsp')"); + run("wreduce", " (skip if '-nodsp')"); + run("xilinx_dsp", " (skip if '-nodsp')"); + run("chtype -set $mul t:$__soft_mul"," (skip if '-nodsp')"); + } + } + + if (check_label("coarse")) { run("alumacc"); run("share"); run("opt"); @@ -408,7 +429,7 @@ struct SynthXilinxPass : public ScriptPass } if (check_label("map_cells")) { - std::string techmap_args = "-map +/techmap.v -D _ABC -map +/xilinx/cells_map.v"; + std::string techmap_args = "-map +/techmap.v -map +/xilinx/cells_map.v"; if (widemux > 0) techmap_args += stringf(" -D MIN_MUX_INPUTS=%d", widemux); run("techmap " + techmap_args); @@ -428,10 +449,12 @@ struct SynthXilinxPass : public ScriptPass if (flatten_before_abc) run("flatten"); if (help_mode) - run("abc -luts 2:2,3,6:5[,10,20] [-dff]", "(option for 'nowidelut', option for '-retime')"); + run("abc -luts 2:2,3,6:5[,10,20] [-dff]", "(option for 'nowidelut'; option for '-retime')"); else if (abc9) { if (family != "xc7") log_warning("'synth_xilinx -abc9' currently supports '-family xc7' only.\n"); + run("techmap -map +/xilinx/abc_map.v -max_iter 1"); + run("read_verilog -icells -lib +/xilinx/abc_model.v"); if (nowidelut) run("abc9 -lut +/xilinx/abc_xc7_nowide.lut -box +/xilinx/abc_xc7.box -W " + std::to_string(XC7_WIRE_DELAY)); else @@ -449,14 +472,15 @@ struct SynthXilinxPass : public ScriptPass // has performed any necessary retiming if (!nosrl || help_mode) run("xilinx_srl -fixed -minlen 3", "(skip if '-nosrl')"); - std::string techmap_args = "-map +/xilinx/lut_map.v -map +/xilinx/cells_map.v"; if (help_mode) - techmap_args += " [-map +/xilinx/ff_map.v]"; - else if (!abc9) - techmap_args += " -map +/xilinx/ff_map.v"; + techmap_args += " [-map +/xilinx/ff_map.v]"; + else if (abc9) + techmap_args += " -map +/xilinx/abc_unmap.v"; + else + techmap_args += " -map +/xilinx/ff_map.v"; run("techmap " + techmap_args); - if (!abc9) + if (!abc9 || help_mode) run("dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT -ff FDSE Q INIT " "-ff FDRE_1 Q INIT -ff FDCE_1 Q INIT -ff FDPE_1 Q INIT -ff FDSE_1 Q INIT", "(without '-abc9' only)"); run("clean"); diff --git a/techlibs/xilinx/tests/.gitignore b/techlibs/xilinx/tests/.gitignore index 496b87461..ef3699bd2 100644 --- a/techlibs/xilinx/tests/.gitignore +++ b/techlibs/xilinx/tests/.gitignore @@ -4,3 +4,8 @@ bram1_[0-9]*/ bram2.log bram2_syn.v bram2_tb +dsp_work*/ +test_dsp_model_ref.v +test_dsp_model_uut.v +test_dsp_model +*.vcd diff --git a/techlibs/xilinx/tests/test_dsp_model.sh b/techlibs/xilinx/tests/test_dsp_model.sh new file mode 100644 index 000000000..2acd97eb4 --- /dev/null +++ b/techlibs/xilinx/tests/test_dsp_model.sh @@ -0,0 +1,14 @@ +#!/bin/bash +set -ex +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 + cat /opt/Xilinx/Vivado/2019.1/data/verilog/src/unisims/DSP48E1.v > test_dsp_model_ref.v +fi +for tb in simd24_preadd_noreg_nocasc simd12_preadd_noreg_nocasc \ + mult_allreg_nopreadd_nocasc mult_noreg_nopreadd_nocasc \ + mult_allreg_preadd_nocasc mult_noreg_preadd_nocasc mult_inreg_preadd_nocasc \ + +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 + vvp -N ./test_dsp_model +done diff --git a/techlibs/xilinx/tests/test_dsp_model.v b/techlibs/xilinx/tests/test_dsp_model.v new file mode 100644 index 000000000..04d5b26ab --- /dev/null +++ b/techlibs/xilinx/tests/test_dsp_model.v @@ -0,0 +1,597 @@ +`timescale 1ns / 1ps + +module testbench; + parameter integer ACASCREG = 1; + parameter integer ADREG = 1; + parameter integer ALUMODEREG = 1; + parameter integer AREG = 1; + parameter AUTORESET_PATDET = "NO_RESET"; + parameter A_INPUT = "DIRECT"; + parameter integer BCASCREG = 1; + parameter integer BREG = 1; + parameter B_INPUT = "DIRECT"; + parameter integer CARRYINREG = 1; + parameter integer CARRYINSELREG = 1; + parameter integer CREG = 1; + parameter integer DREG = 1; + parameter integer INMODEREG = 1; + parameter integer MREG = 1; + parameter integer OPMODEREG = 1; + parameter integer PREG = 1; + parameter SEL_MASK = "MASK"; + parameter SEL_PATTERN = "PATTERN"; + parameter USE_DPORT = "FALSE"; + parameter USE_MULT = "MULTIPLY"; + parameter USE_PATTERN_DETECT = "NO_PATDET"; + parameter USE_SIMD = "ONE48"; + parameter [47:0] MASK = 48'h3FFFFFFFFFFF; + parameter [47:0] PATTERN = 48'h000000000000; + parameter [3:0] IS_ALUMODE_INVERTED = 4'b0; + parameter [0:0] IS_CARRYIN_INVERTED = 1'b0; + parameter [0:0] IS_CLK_INVERTED = 1'b0; + parameter [4:0] IS_INMODE_INVERTED = 5'b0; + parameter [6:0] IS_OPMODE_INVERTED = 7'b0; + + reg CLK; + reg CEA1, CEA2, CEAD, CEALUMODE, CEB1, CEB2, CEC, CECARRYIN, CECTRL; + reg CED, CEINMODE, CEM, CEP; + reg RSTA, RSTALLCARRYIN, RSTALUMODE, RSTB, RSTC, RSTCTRL, RSTD, RSTINMODE, RSTM, RSTP; + reg [29:0] A, ACIN; + reg [17:0] B, BCIN; + reg [47:0] C; + reg [24:0] D; + reg [47:0] PCIN; + reg [3:0] ALUMODE; + reg [2:0] CARRYINSEL; + reg [4:0] INMODE; + reg [6:0] OPMODE; + reg CARRYCASCIN, CARRYIN, MULTSIGNIN; + + output [29:0] ACOUT, REF_ACOUT; + output [17:0] BCOUT, REF_BCOUT; + output CARRYCASCOUT, REF_CARRYCASCOUT; + output [3:0] CARRYOUT, REF_CARRYOUT; + output MULTSIGNOUT, REF_MULTSIGNOUT; + output OVERFLOW, REF_OVERFLOW; + output [47:0] P, REF_P; + output PATTERNBDETECT, REF_PATTERNBDETECT; + output PATTERNDETECT, REF_PATTERNDETECT; + output [47:0] PCOUT, REF_PCOUT; + output UNDERFLOW, REF_UNDERFLOW; + + integer errcount = 0; + + reg ERROR_FLAG = 0; + + task clkcycle; + begin + #5; + CLK = ~CLK; + #10; + CLK = ~CLK; + #2; + ERROR_FLAG = 0; + if (REF_P !== P) begin + $display("ERROR at %1t: REF_P=%b UUT_P=%b DIFF=%b", $time, REF_P, P, REF_P ^ P); + errcount = errcount + 1; + ERROR_FLAG = 1; + end + if (REF_CARRYOUT !== CARRYOUT) begin + $display("ERROR at %1t: REF_CARRYOUT=%b UUT_CARRYOUT=%b", $time, REF_CARRYOUT, CARRYOUT); + errcount = errcount + 1; + ERROR_FLAG = 1; + end + #3; + end + endtask + + reg config_valid = 0; + task drc; + begin + config_valid = 1; + if (AREG != 2 && INMODE[0]) config_valid = 0; + if (BREG != 2 && INMODE[4]) config_valid = 0; + + if (USE_SIMD != "ONE48" && OPMODE[3:0] == 4'b0101) config_valid = 0; + + if (OPMODE[1:0] == 2'b10 && PREG != 1) config_valid = 0; + if ((OPMODE[3:2] == 2'b01) ^ (OPMODE[1:0] == 2'b01) == 1'b1) config_valid = 0; + if ((OPMODE[6:4] == 3'b010 || OPMODE[6:4] == 3'b110) && PREG != 1) config_valid = 0; + if ((OPMODE[6:4] == 3'b100) && (PREG != 1 || OPMODE[3:0] != 4'b1000 || ALUMODE[3:2] == 2'b01 || ALUMODE[3:2] == 2'b11)) config_valid = 0; + if ((CARRYINSEL == 3'b100 || CARRYINSEL == 3'b101 || CARRYINSEL == 3'b111) && (PREG != 1)) config_valid = 0; + if (OPMODE[6:4] == 3'b111) config_valid = 0; + if ((OPMODE[3:0] == 4'b0101) && CARRYINSEL == 3'b010) config_valid = 0; + if (CARRYINSEL == 3'b000 && OPMODE == 7'b1001000) config_valid = 0; + + if ((ALUMODE[3:2] == 2'b01 || ALUMODE[3:2] == 2'b11) && OPMODE[3:2] != 2'b00 && OPMODE[3:2] != 2'b10) config_valid = 0; + + + end + endtask + + initial begin + $dumpfile("test_dsp_model.vcd"); + $dumpvars(0, testbench); + + #2; + CLK = 1'b0; + {CEA1, CEA2, CEAD, CEALUMODE, CEB1, CEB2, CEC, CECARRYIN, CECTRL} = 9'b111111111; + {CED, CEINMODE, CEM, CEP} = 4'b1111; + + {A, B, C, D} = 0; + {ACIN, BCIN, PCIN} = 0; + {ALUMODE, CARRYINSEL, INMODE} = 0; + {OPMODE, CARRYCASCIN, CARRYIN, MULTSIGNIN} = 0; + + {RSTA, RSTALLCARRYIN, RSTALUMODE, RSTB, RSTC, RSTCTRL, RSTD, RSTINMODE, RSTM, RSTP} = ~0; + repeat (10) begin + #10; + CLK = 1'b1; + #10; + CLK = 1'b0; + #10; + CLK = 1'b1; + #10; + CLK = 1'b0; + end + {RSTA, RSTALLCARRYIN, RSTALUMODE, RSTB, RSTC, RSTCTRL, RSTD, RSTINMODE, RSTM, RSTP} = 0; + + repeat (10000) begin + clkcycle; + config_valid = 0; + while (!config_valid) begin + A = $urandom; + ACIN = $urandom; + B = $urandom; + BCIN = $urandom; + C = {$urandom, $urandom}; + D = $urandom; + PCIN = {$urandom, $urandom}; + + {CEA1, CEA2, CEAD, CEALUMODE, CEB1, CEB2, CEC, CECARRYIN, CECTRL} = $urandom | $urandom | $urandom; + {CED, CEINMODE, CEM, CEP} = $urandom | $urandom | $urandom | $urandom; + + // Otherwise we can accidentally create illegal configs + CEINMODE = CECTRL; + CEALUMODE = CECTRL; + + {RSTA, RSTALLCARRYIN, RSTALUMODE, RSTB, RSTC, RSTCTRL, RSTD, RSTINMODE, RSTM, RSTP} = $urandom & $urandom & $urandom & $urandom & $urandom & $urandom; + {ALUMODE, INMODE} = $urandom; + CARRYINSEL = $urandom & $urandom & $urandom; + OPMODE = $urandom; + if ($urandom & 1'b1) + OPMODE[3:0] = 4'b0101; // test multiply more than other modes + {CARRYCASCIN, CARRYIN, MULTSIGNIN} = $urandom; + + // So few valid options in these modes, just force one valid option + if (CARRYINSEL == 3'b001) OPMODE = 7'b1010101; + if (CARRYINSEL == 3'b010) OPMODE = 7'b0001010; + if (CARRYINSEL == 3'b011) OPMODE = 7'b0011011; + if (CARRYINSEL == 3'b100) OPMODE = 7'b0110011; + if (CARRYINSEL == 3'b101) OPMODE = 7'b0011010; + if (CARRYINSEL == 3'b110) OPMODE = 7'b0010101; + if (CARRYINSEL == 3'b111) OPMODE = 7'b0100011; + + drc; + end + end + + if (errcount == 0) begin + $display("All tests passed."); + $finish; + end else begin + $display("Caught %1d errors.", errcount); + $stop; + end + end + + DSP48E1 #( + .ACASCREG (ACASCREG), + .ADREG (ADREG), + .ALUMODEREG (ALUMODEREG), + .AREG (AREG), + .AUTORESET_PATDET (AUTORESET_PATDET), + .A_INPUT (A_INPUT), + .BCASCREG (BCASCREG), + .BREG (BREG), + .B_INPUT (B_INPUT), + .CARRYINREG (CARRYINREG), + .CARRYINSELREG (CARRYINSELREG), + .CREG (CREG), + .DREG (DREG), + .INMODEREG (INMODEREG), + .MREG (MREG), + .OPMODEREG (OPMODEREG), + .PREG (PREG), + .SEL_MASK (SEL_MASK), + .SEL_PATTERN (SEL_PATTERN), + .USE_DPORT (USE_DPORT), + .USE_MULT (USE_MULT), + .USE_PATTERN_DETECT (USE_PATTERN_DETECT), + .USE_SIMD (USE_SIMD), + .MASK (MASK), + .PATTERN (PATTERN), + .IS_ALUMODE_INVERTED(IS_ALUMODE_INVERTED), + .IS_CARRYIN_INVERTED(IS_CARRYIN_INVERTED), + .IS_CLK_INVERTED (IS_CLK_INVERTED), + .IS_INMODE_INVERTED (IS_INMODE_INVERTED), + .IS_OPMODE_INVERTED (IS_OPMODE_INVERTED) + ) ref ( + .ACOUT (REF_ACOUT), + .BCOUT (REF_BCOUT), + .CARRYCASCOUT (REF_CARRYCASCOUT), + .CARRYOUT (REF_CARRYOUT), + .MULTSIGNOUT (REF_MULTSIGNOUT), + .OVERFLOW (REF_OVERFLOW), + .P (REF_P), + .PATTERNBDETECT(REF_PATTERNBDETECT), + .PATTERNDETECT (REF_PATTERNDETECT), + .PCOUT (REF_PCOUT), + .UNDERFLOW (REF_UNDERFLOW), + .A (A), + .ACIN (ACIN), + .ALUMODE (ALUMODE), + .B (B), + .BCIN (BCIN), + .C (C), + .CARRYCASCIN (CARRYCASCIN), + .CARRYINSEL (CARRYINSEL), + .CEA1 (CEA1), + .CEA2 (CEA2), + .CEAD (CEAD), + .CEALUMODE (CEALUMODE), + .CEB1 (CEB1), + .CEB2 (CEB2), + .CEC (CEC), + .CECARRYIN (CECARRYIN), + .CECTRL (CECTRL), + .CED (CED), + .CEINMODE (CEINMODE), + .CEM (CEM), + .CEP (CEP), + .CLK (CLK), + .D (D), + .INMODE (INMODE), + .MULTSIGNIN (MULTSIGNIN), + .OPMODE (OPMODE), + .PCIN (PCIN), + .RSTA (RSTA), + .RSTALLCARRYIN (RSTALLCARRYIN), + .RSTALUMODE (RSTALUMODE), + .RSTB (RSTB), + .RSTC (RSTC), + .RSTCTRL (RSTCTRL), + .RSTD (RSTD), + .RSTINMODE (RSTINMODE), + .RSTM (RSTM), + .RSTP (RSTP) + ); + + DSP48E1_UUT #( + .ACASCREG (ACASCREG), + .ADREG (ADREG), + .ALUMODEREG (ALUMODEREG), + .AREG (AREG), + .AUTORESET_PATDET (AUTORESET_PATDET), + .A_INPUT (A_INPUT), + .BCASCREG (BCASCREG), + .BREG (BREG), + .B_INPUT (B_INPUT), + .CARRYINREG (CARRYINREG), + .CARRYINSELREG (CARRYINSELREG), + .CREG (CREG), + .DREG (DREG), + .INMODEREG (INMODEREG), + .MREG (MREG), + .OPMODEREG (OPMODEREG), + .PREG (PREG), + .SEL_MASK (SEL_MASK), + .SEL_PATTERN (SEL_PATTERN), + .USE_DPORT (USE_DPORT), + .USE_MULT (USE_MULT), + .USE_PATTERN_DETECT (USE_PATTERN_DETECT), + .USE_SIMD (USE_SIMD), + .MASK (MASK), + .PATTERN (PATTERN), + .IS_ALUMODE_INVERTED(IS_ALUMODE_INVERTED), + .IS_CARRYIN_INVERTED(IS_CARRYIN_INVERTED), + .IS_CLK_INVERTED (IS_CLK_INVERTED), + .IS_INMODE_INVERTED (IS_INMODE_INVERTED), + .IS_OPMODE_INVERTED (IS_OPMODE_INVERTED) + ) uut ( + .ACOUT (ACOUT), + .BCOUT (BCOUT), + .CARRYCASCOUT (CARRYCASCOUT), + .CARRYOUT (CARRYOUT), + .MULTSIGNOUT (MULTSIGNOUT), + .OVERFLOW (OVERFLOW), + .P (P), + .PATTERNBDETECT(PATTERNBDETECT), + .PATTERNDETECT (PATTERNDETECT), + .PCOUT (PCOUT), + .UNDERFLOW (UNDERFLOW), + .A (A), + .ACIN (ACIN), + .ALUMODE (ALUMODE), + .B (B), + .BCIN (BCIN), + .C (C), + .CARRYCASCIN (CARRYCASCIN), + .CARRYINSEL (CARRYINSEL), + .CEA1 (CEA1), + .CEA2 (CEA2), + .CEAD (CEAD), + .CEALUMODE (CEALUMODE), + .CEB1 (CEB1), + .CEB2 (CEB2), + .CEC (CEC), + .CECARRYIN (CECARRYIN), + .CECTRL (CECTRL), + .CED (CED), + .CEINMODE (CEINMODE), + .CEM (CEM), + .CEP (CEP), + .CLK (CLK), + .D (D), + .INMODE (INMODE), + .MULTSIGNIN (MULTSIGNIN), + .OPMODE (OPMODE), + .PCIN (PCIN), + .RSTA (RSTA), + .RSTALLCARRYIN (RSTALLCARRYIN), + .RSTALUMODE (RSTALUMODE), + .RSTB (RSTB), + .RSTC (RSTC), + .RSTCTRL (RSTCTRL), + .RSTD (RSTD), + .RSTINMODE (RSTINMODE), + .RSTM (RSTM), + .RSTP (RSTP) + ); +endmodule + +module mult_noreg_nopreadd_nocasc; + testbench #( + .ACASCREG (0), + .ADREG (0), + .ALUMODEREG (0), + .AREG (0), + .AUTORESET_PATDET ("NO_RESET"), + .A_INPUT ("DIRECT"), + .BCASCREG (0), + .BREG (0), + .B_INPUT ("DIRECT"), + .CARRYINREG (0), + .CARRYINSELREG (0), + .CREG (0), + .DREG (0), + .INMODEREG (0), + .MREG (0), + .OPMODEREG (0), + .PREG (0), + .SEL_MASK ("MASK"), + .SEL_PATTERN ("PATTERN"), + .USE_DPORT ("FALSE"), + .USE_MULT ("DYNAMIC"), + .USE_PATTERN_DETECT ("NO_PATDET"), + .USE_SIMD ("ONE48"), + .MASK (48'h3FFFFFFFFFFF), + .PATTERN (48'h000000000000), + .IS_ALUMODE_INVERTED(4'b0), + .IS_CARRYIN_INVERTED(1'b0), + .IS_CLK_INVERTED (1'b0), + .IS_INMODE_INVERTED (5'b0), + .IS_OPMODE_INVERTED (7'b0) + ) testbench (); +endmodule + +module mult_allreg_nopreadd_nocasc; + testbench #( + .ACASCREG (1), + .ADREG (1), + .ALUMODEREG (1), + .AREG (2), + .AUTORESET_PATDET ("NO_RESET"), + .A_INPUT ("DIRECT"), + .BCASCREG (1), + .BREG (2), + .B_INPUT ("DIRECT"), + .CARRYINREG (1), + .CARRYINSELREG (1), + .CREG (1), + .DREG (1), + .INMODEREG (1), + .MREG (1), + .OPMODEREG (1), + .PREG (1), + .SEL_MASK ("MASK"), + .SEL_PATTERN ("PATTERN"), + .USE_DPORT ("FALSE"), + .USE_MULT ("DYNAMIC"), + .USE_PATTERN_DETECT ("NO_PATDET"), + .USE_SIMD ("ONE48"), + .MASK (48'h3FFFFFFFFFFF), + .PATTERN (48'h000000000000), + .IS_ALUMODE_INVERTED(4'b0), + .IS_CARRYIN_INVERTED(1'b0), + .IS_CLK_INVERTED (1'b0), + .IS_INMODE_INVERTED (5'b0), + .IS_OPMODE_INVERTED (7'b0) + ) testbench (); +endmodule + +module mult_noreg_preadd_nocasc; + testbench #( + .ACASCREG (0), + .ADREG (0), + .ALUMODEREG (0), + .AREG (0), + .AUTORESET_PATDET ("NO_RESET"), + .A_INPUT ("DIRECT"), + .BCASCREG (0), + .BREG (0), + .B_INPUT ("DIRECT"), + .CARRYINREG (0), + .CARRYINSELREG (0), + .CREG (0), + .DREG (0), + .INMODEREG (0), + .MREG (0), + .OPMODEREG (0), + .PREG (0), + .SEL_MASK ("MASK"), + .SEL_PATTERN ("PATTERN"), + .USE_DPORT ("TRUE"), + .USE_MULT ("DYNAMIC"), + .USE_PATTERN_DETECT ("NO_PATDET"), + .USE_SIMD ("ONE48"), + .MASK (48'h3FFFFFFFFFFF), + .PATTERN (48'h000000000000), + .IS_ALUMODE_INVERTED(4'b0), + .IS_CARRYIN_INVERTED(1'b0), + .IS_CLK_INVERTED (1'b0), + .IS_INMODE_INVERTED (5'b0), + .IS_OPMODE_INVERTED (7'b0) + ) testbench (); +endmodule + +module mult_allreg_preadd_nocasc; + testbench #( + .ACASCREG (1), + .ADREG (1), + .ALUMODEREG (1), + .AREG (2), + .AUTORESET_PATDET ("NO_RESET"), + .A_INPUT ("DIRECT"), + .BCASCREG (1), + .BREG (2), + .B_INPUT ("DIRECT"), + .CARRYINREG (1), + .CARRYINSELREG (1), + .CREG (1), + .DREG (1), + .INMODEREG (1), + .MREG (1), + .OPMODEREG (1), + .PREG (1), + .SEL_MASK ("MASK"), + .SEL_PATTERN ("PATTERN"), + .USE_DPORT ("TRUE"), + .USE_MULT ("DYNAMIC"), + .USE_PATTERN_DETECT ("NO_PATDET"), + .USE_SIMD ("ONE48"), + .MASK (48'h3FFFFFFFFFFF), + .PATTERN (48'h000000000000), + .IS_ALUMODE_INVERTED(4'b0), + .IS_CARRYIN_INVERTED(1'b0), + .IS_CLK_INVERTED (1'b0), + .IS_INMODE_INVERTED (5'b0), + .IS_OPMODE_INVERTED (7'b0) + ) testbench (); +endmodule + +module mult_inreg_preadd_nocasc; + testbench #( + .ACASCREG (1), + .ADREG (0), + .ALUMODEREG (0), + .AREG (1), + .AUTORESET_PATDET ("NO_RESET"), + .A_INPUT ("DIRECT"), + .BCASCREG (1), + .BREG (1), + .B_INPUT ("DIRECT"), + .CARRYINREG (0), + .CARRYINSELREG (0), + .CREG (1), + .DREG (1), + .INMODEREG (0), + .MREG (0), + .OPMODEREG (0), + .PREG (0), + .SEL_MASK ("MASK"), + .SEL_PATTERN ("PATTERN"), + .USE_DPORT ("TRUE"), + .USE_MULT ("DYNAMIC"), + .USE_PATTERN_DETECT ("NO_PATDET"), + .USE_SIMD ("ONE48"), + .MASK (48'h3FFFFFFFFFFF), + .PATTERN (48'h000000000000), + .IS_ALUMODE_INVERTED(4'b0), + .IS_CARRYIN_INVERTED(1'b0), + .IS_CLK_INVERTED (1'b0), + .IS_INMODE_INVERTED (5'b0), + .IS_OPMODE_INVERTED (7'b0) + ) testbench (); +endmodule + +module simd12_preadd_noreg_nocasc; + testbench #( + .ACASCREG (0), + .ADREG (0), + .ALUMODEREG (0), + .AREG (0), + .AUTORESET_PATDET ("NO_RESET"), + .A_INPUT ("DIRECT"), + .BCASCREG (0), + .BREG (0), + .B_INPUT ("DIRECT"), + .CARRYINREG (0), + .CARRYINSELREG (0), + .CREG (0), + .DREG (0), + .INMODEREG (0), + .MREG (0), + .OPMODEREG (0), + .PREG (0), + .SEL_MASK ("MASK"), + .SEL_PATTERN ("PATTERN"), + .USE_DPORT ("TRUE"), + .USE_MULT ("DYNAMIC"), + .USE_PATTERN_DETECT ("NO_PATDET"), + .USE_SIMD ("FOUR12"), + .MASK (48'h3FFFFFFFFFFF), + .PATTERN (48'h000000000000), + .IS_ALUMODE_INVERTED(4'b0), + .IS_CARRYIN_INVERTED(1'b0), + .IS_CLK_INVERTED (1'b0), + .IS_INMODE_INVERTED (5'b0), + .IS_OPMODE_INVERTED (7'b0) + ) testbench (); +endmodule + + +module simd24_preadd_noreg_nocasc; + testbench #( + .ACASCREG (0), + .ADREG (0), + .ALUMODEREG (0), + .AREG (0), + .AUTORESET_PATDET ("NO_RESET"), + .A_INPUT ("DIRECT"), + .BCASCREG (0), + .BREG (0), + .B_INPUT ("DIRECT"), + .CARRYINREG (0), + .CARRYINSELREG (0), + .CREG (0), + .DREG (0), + .INMODEREG (0), + .MREG (0), + .OPMODEREG (0), + .PREG (0), + .SEL_MASK ("MASK"), + .SEL_PATTERN ("PATTERN"), + .USE_DPORT ("TRUE"), + .USE_MULT ("DYNAMIC"), + .USE_PATTERN_DETECT ("NO_PATDET"), + .USE_SIMD ("TWO24"), + .MASK (48'h3FFFFFFFFFFF), + .PATTERN (48'h000000000000), + .IS_ALUMODE_INVERTED(4'b0), + .IS_CARRYIN_INVERTED(1'b0), + .IS_CLK_INVERTED (1'b0), + .IS_INMODE_INVERTED (5'b0), + .IS_OPMODE_INVERTED (7'b0) + ) testbench (); +endmodule
\ No newline at end of file diff --git a/techlibs/xilinx/xc7_brams_bb.v b/techlibs/xilinx/xc7_brams_bb.v index a43b4b5a1..5b40a457d 100644 --- a/techlibs/xilinx/xc7_brams_bb.v +++ b/techlibs/xilinx/xc7_brams_bb.v @@ -1,3 +1,5 @@ +// Max delays from https://github.com/SymbiFlow/prjxray-db/blob/f8e0364116b2983ac72a3dc8c509ea1cc79e2e3d/artix7/timings/BRAM_L.sdf#L138-L147 + module RAMB18E1 ( (* clkbuf_sink *) input CLKARDCLK, @@ -21,9 +23,13 @@ module RAMB18E1 ( input [1:0] WEA, input [3:0] WEBWE, + (* abc_arrival=2454 *) output [15:0] DOADO, + (* abc_arrival=2454 *) output [15:0] DOBDO, + (* abc_arrival=2454 *) output [1:0] DOPADOP, + (* abc_arrival=2454 *) output [1:0] DOPBDOP ); parameter INITP_00 = 256'h0000000000000000000000000000000000000000000000000000000000000000; @@ -147,9 +153,13 @@ module RAMB36E1 ( input [3:0] WEA, input [7:0] WEBWE, + (* abc_arrival=2454 *) output [31:0] DOADO, + (* abc_arrival=2454 *) output [31:0] DOBDO, + (* abc_arrival=2454 *) output [3:0] DOPADOP, + (* abc_arrival=2454 *) output [3:0] DOPBDOP ); parameter INITP_00 = 256'h0000000000000000000000000000000000000000000000000000000000000000; diff --git a/tests/various/abc9.v b/tests/various/abc9.v index a08b613a8..30ebd4e26 100644 --- a/tests/various/abc9.v +++ b/tests/various/abc9.v @@ -5,5 +5,7 @@ always @* endmodule module abc9_test028(input i, output o); -unknown u(~i, o); +wire w; +unknown u(~i, w); +unknown2 u2(w, o); endmodule diff --git a/tests/xilinx/macc.v b/tests/xilinx/macc.v new file mode 100644 index 000000000..bae63b5a4 --- /dev/null +++ b/tests/xilinx/macc.v @@ -0,0 +1,37 @@ +// Signed 40-bit streaming accumulator with 16-bit inputs +// File: HDL_Coding_Techniques/multipliers/multipliers4.v +// +module macc # (parameter SIZEIN = /*16*/7, SIZEOUT = 40) + (input clk, ce, sload, + input signed [SIZEIN-1:0] a, b, + output signed [SIZEOUT-1:0] accum_out); + // Declare registers for intermediate values + reg signed [SIZEIN-1:0] a_reg, b_reg; + reg sload_reg; + reg signed [2*SIZEIN:0] mult_reg; + reg signed [SIZEOUT-1:0] adder_out, old_result; + always @(adder_out or sload_reg) begin + //if (sload_reg) + //old_result <= 0; + //else + // 'sload' is now active (=low) and opens the accumulation loop. + // The accumulator takes the next multiplier output in + // the same cycle. + old_result <= adder_out; + a_reg <= a; + b_reg <= b; + end + + always @(posedge clk) + //if (ce) + begin + mult_reg <= a_reg * b_reg; + sload_reg <= sload; + // Store accumulation result into a register + adder_out <= old_result + mult_reg; + end + + // Output accumulation result + assign accum_out = adder_out; + +endmodule // macc diff --git a/tests/xilinx/macc.ys b/tests/xilinx/macc.ys new file mode 100644 index 000000000..62b69f4d2 --- /dev/null +++ b/tests/xilinx/macc.ys @@ -0,0 +1,17 @@ +read_verilog macc.v +proc +hierarchy -top macc +equiv_opt -run :restore -map +/xilinx/cells_sim.v synth_xilinx # equivalency check + +#equiv_miter -trigger miter equiv +#sat -verify -prove-asserts -tempinduct -show-inputs -show-outputs miter + +#equiv_opt -assert -run :prove -map +/xilinx/cells_sim.v synth_xilinx # equivalency check +#miter -equiv -flatten -make_assert -make_outputs gold gate miter +#sat -set-init-zero -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) +cd macc # Constrain all select calls below inside the top module +select -assert-count 1 t:BUFG +select -assert-count 1 t:DSP48E1 +select -assert-none t:BUFG t:DSP48E1 %% t:* %D diff --git a/tests/xilinx/mul_unsigned.v b/tests/xilinx/mul_unsigned.v new file mode 100644 index 000000000..e3713a642 --- /dev/null +++ b/tests/xilinx/mul_unsigned.v @@ -0,0 +1,30 @@ +/* +Example from: https://www.xilinx.com/support/documentation/sw_manuals/xilinx2019_1/ug901-vivado-synthesis.pdf [p. 89]. +*/ + +// Unsigned 16x24-bit Multiplier +// 1 latency stage on operands +// 3 latency stage after the multiplication +// File: multipliers2.v +// +module mul_unsigned (clk, A, B, RES); +parameter WIDTHA = /*16*/ 6; +parameter WIDTHB = /*24*/ 9; +input clk; +input [WIDTHA-1:0] A; +input [WIDTHB-1:0] B; +output [WIDTHA+WIDTHB-1:0] RES; +reg [WIDTHA-1:0] rA; +reg [WIDTHB-1:0] rB; +reg [WIDTHA+WIDTHB-1:0] M [3:0]; +integer i; +always @(posedge clk) + begin + rA <= A; + rB <= B; + M[0] <= rA * rB; + for (i = 0; i < 3; i = i+1) + M[i+1] <= M[i]; + end +assign RES = M[3]; +endmodule diff --git a/tests/xilinx/mul_unsigned.ys b/tests/xilinx/mul_unsigned.ys new file mode 100644 index 000000000..30c034afe --- /dev/null +++ b/tests/xilinx/mul_unsigned.ys @@ -0,0 +1,11 @@ +read_verilog mul_unsigned.v +proc +hierarchy -top mul_unsigned +equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx # 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 +stat +select -assert-count 1 t:BUFG +select -assert-count 1 t:DSP48E1 +select -assert-count 30 t:FDRE +select -assert-none t:DSP48E1 t:FDRE t:BUFG %% t:* %D |