diff options
56 files changed, 1920 insertions, 293 deletions
@@ -14,11 +14,16 @@ Yosys 0.9 .. Yosys 0.9-dev - Added "synth -abc9" (experimental) - Added "script -scriptwire - "synth_xilinx" to now infer wide multiplexers (-widemux <min> to enable) + - Renamed labels/options in synth_ice40 (e.g. dram -> map_lutram; -nodram -> -nolutram) + - Renamed labels/options in synth_ecp5 (e.g. dram -> map_lutram; -nodram -> -nolutram) + - Renamed labels in synth_intel (e.g. bram -> map_bram) + - Renamed labels/options in synth_xilinx (e.g. dram -> map_lutram; -nodram -> -nolutram) - Added automatic gzip decompression for frontends - Added $_NMUX_ cell type - Added automatic gzip compression (based on filename extension) for backends - Improve attribute and parameter encoding in JSON to avoid ambiguities between bit vectors and strings containing [01xz]* + - Improvements in pmgen: subpattern and recursive matches - Added "opt_share" pass, run as part of "opt -full" - Added "ice40_wrapcarry" to encapsulate SB_LUT+SB_CARRY pairs for techmapping - Removed "ice40_unlut" @@ -487,6 +487,11 @@ define add_include_file $(eval $(call add_share_file,$(dir share/include/$(1)),$(1))) endef +define add_extra_objs +EXTRA_OBJS += $(1) +.SECONDARY: $(1) +endef + ifeq ($(PRETTY), 1) P_STATUS = 0 P_OFFSET = 0 @@ -682,6 +687,7 @@ endif test: $(TARGETS) $(EXTRA_TARGETS) +cd tests/simple && bash run-test.sh $(SEEDOPT) + +cd tests/simple_abc9 && bash run-test.sh $(SEEDOPT) +cd tests/hana && bash run-test.sh $(SEEDOPT) +cd tests/asicworld && bash run-test.sh $(SEEDOPT) # +cd tests/realmath && bash run-test.sh $(SEEDOPT) @@ -694,10 +700,10 @@ test: $(TARGETS) $(EXTRA_TARGETS) +cd tests/various && bash run-test.sh +cd tests/sat && bash run-test.sh +cd tests/svinterfaces && bash run-test.sh $(SEEDOPT) + +cd tests/proc && bash run-test.sh +cd tests/opt && bash run-test.sh +cd tests/aiger && bash run-test.sh $(ABCOPT) +cd tests/arch && bash run-test.sh - +cd tests/simple_abc9 && bash run-test.sh $(SEEDOPT) @echo "" @echo " Passed \"make test\"." @echo "" @@ -405,6 +405,23 @@ Verilog Attributes and non-standard features blackboxes and whiteboxes. Use ``read_verilog -specify`` to enable this functionality. (By default specify .. endspecify blocks are ignored.) +- The module attribute ``abc_box_id`` specifies a positive integer linking a + 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. + Non-standard or SystemVerilog features for formal verification ============================================================== diff --git a/backends/aiger/xaiger.cc b/backends/aiger/xaiger.cc index 5e12e9a34..77d0e94c2 100644 --- a/backends/aiger/xaiger.cc +++ b/backends/aiger/xaiger.cc @@ -326,7 +326,6 @@ struct XAigerWriter #endif log_assert(no_loops); - pool<IdString> seen_boxes; for (auto cell_name : toposort.sorted) { RTLIL::Cell *cell = module->cell(cell_name); log_assert(cell); @@ -335,47 +334,6 @@ struct XAigerWriter if (!box_module || !box_module->attributes.count("\\abc_box_id")) continue; - if (seen_boxes.insert(cell->type).second) { - auto it = box_module->attributes.find("\\abc_carry"); - if (it != box_module->attributes.end()) { - RTLIL::Wire *carry_in = nullptr, *carry_out = nullptr; - auto carry_in_out = it->second.decode_string(); - auto pos = carry_in_out.find(','); - if (pos == std::string::npos) - log_error("'abc_carry' attribute on module '%s' does not contain ','.\n", log_id(cell->type)); - auto carry_in_name = RTLIL::escape_id(carry_in_out.substr(0, pos)); - carry_in = box_module->wire(carry_in_name); - if (!carry_in || !carry_in->port_input) - log_error("'abc_carry' on module '%s' contains '%s' which does not exist or is not an input port.\n", log_id(cell->type), carry_in_name.c_str()); - - auto carry_out_name = RTLIL::escape_id(carry_in_out.substr(pos+1)); - carry_out = box_module->wire(carry_out_name); - if (!carry_out || !carry_out->port_output) - log_error("'abc_carry' on module '%s' contains '%s' which does not exist or is not an output port.\n", log_id(cell->type), carry_out_name.c_str()); - - auto &ports = box_module->ports; - for (auto jt = ports.begin(); jt != ports.end(); ) { - RTLIL::Wire* w = box_module->wire(*jt); - log_assert(w); - if (w == carry_in || w == carry_out) { - jt = ports.erase(jt); - continue; - } - if (w->port_id > carry_in->port_id) - --w->port_id; - if (w->port_id > carry_out->port_id) - --w->port_id; - log_assert(w->port_input || w->port_output); - log_assert(ports[w->port_id-1] == w->name); - ++jt; - } - ports.push_back(carry_in->name); - carry_in->port_id = ports.size(); - ports.push_back(carry_out->name); - carry_out->port_id = ports.size(); - } - } - // Fully pad all unused input connections of this box cell with S0 // Fully pad all undriven output connections of this box cell with anonymous wires // NB: Assume box_module->ports are sorted alphabetically diff --git a/frontends/aiger/aigerparse.cc b/frontends/aiger/aigerparse.cc index ac9e31f70..06522939f 100644 --- a/frontends/aiger/aigerparse.cc +++ b/frontends/aiger/aigerparse.cc @@ -451,7 +451,7 @@ void AigerReader::parse_xaiger(const dict<int,IdString> &box_lookup) uint32_t poNum YS_ATTRIBUTE(unused) = parse_xaiger_literal(f); log_debug("poNum = %u\n", poNum); uint32_t boxNum = parse_xaiger_literal(f); - log_debug("boxNum = %u\n", poNum); + log_debug("boxNum = %u\n", boxNum); for (unsigned i = 0; i < boxNum; i++) { f.ignore(2*sizeof(uint32_t)); uint32_t boxUniqueId = parse_xaiger_literal(f); diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index 0d6626b19..82283fb5b 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -1502,7 +1502,10 @@ std::string AstModule::derive_common(RTLIL::Design *design, dict<RTLIL::IdString rewrite_parameter: para_info += stringf("%s=%s", child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id]))); delete child->children.at(0); - if ((parameters[para_id].flags & RTLIL::CONST_FLAG_STRING) != 0) + if ((parameters[para_id].flags & RTLIL::CONST_FLAG_REAL) != 0) { + child->children[0] = new AstNode(AST_REALVALUE); + child->children[0]->realvalue = std::stod(parameters[para_id].decode_string()); + } else if ((parameters[para_id].flags & RTLIL::CONST_FLAG_STRING) != 0) child->children[0] = AstNode::mkconst_str(parameters[para_id].decode_string()); else child->children[0] = AstNode::mkconst_bits(parameters[para_id].bits, (parameters[para_id].flags & RTLIL::CONST_FLAG_SIGNED) != 0); diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 54b9efaad..8493aa513 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -150,6 +150,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, reg->str = stringf("%s[%d]", node->str.c_str(), i); reg->is_reg = true; reg->is_signed = node->is_signed; + for (auto &it : node->attributes) + reg->attributes.emplace(it.first, it.second->clone()); + reg->filename = node->filename; + reg->linenum = node->linenum; children.push_back(reg); while (reg->simplify(true, false, false, 1, -1, false, false)) { } } diff --git a/passes/opt/opt_share.cc b/passes/opt/opt_share.cc index 734cbcf81..c53fb3113 100644 --- a/passes/opt/opt_share.cc +++ b/passes/opt/opt_share.cc @@ -127,7 +127,7 @@ bool mergeable(RTLIL::Cell *a, RTLIL::Cell *b) RTLIL::IdString decode_port_semantics(RTLIL::Cell *cell, RTLIL::IdString port_name) { - if (cell->type.in(ID($lt), ID($le), ID($ge), ID($gt), ID($div), ID($mod), ID($concat), SHIFT_OPS) && port_name == ID(B)) + if (cell->type.in(ID($lt), ID($le), ID($ge), ID($gt), ID($div), ID($mod), ID($concat), SHIFT_OPS) && port_name == ID::B) return port_name; return ""; @@ -135,9 +135,9 @@ RTLIL::IdString decode_port_semantics(RTLIL::Cell *cell, RTLIL::IdString port_na RTLIL::SigSpec decode_port_sign(RTLIL::Cell *cell, RTLIL::IdString port_name) { - if (cell->type == ID($alu) && port_name == ID(B)) + if (cell->type == ID($alu) && port_name == ID::B) return cell->getPort(ID(BI)); - else if (cell->type == ID($sub) && port_name == ID(B)) + else if (cell->type == ID($sub) && port_name == ID::B) return RTLIL::Const(1, 1); return RTLIL::Const(0, 1); @@ -173,9 +173,9 @@ void merge_operators(RTLIL::Module *module, RTLIL::Cell *mux, const std::vector< for (const auto& p : ports) { auto op = p.op; - RTLIL::IdString muxed_port_name = ID(A); - if (decode_port(op, ID(A), &assign_map) == operand) - muxed_port_name = ID(B); + RTLIL::IdString muxed_port_name = ID::A; + if (decode_port(op, ID::A, &assign_map) == operand) + muxed_port_name = ID::B; auto operand = decode_port(op, muxed_port_name, &assign_map); if (operand.sig.size() > max_width) @@ -204,9 +204,9 @@ void merge_operators(RTLIL::Module *module, RTLIL::Cell *mux, const std::vector< if (muxed_op.sign != muxed_operands[0].sign) muxed_op = ExtSigSpec(module->Neg(NEW_ID, muxed_op.sig, muxed_op.is_signed)); - RTLIL::SigSpec mux_y = mux->getPort(ID(Y)); - RTLIL::SigSpec mux_a = mux->getPort(ID(A)); - RTLIL::SigSpec mux_b = mux->getPort(ID(B)); + RTLIL::SigSpec mux_y = mux->getPort(ID::Y); + RTLIL::SigSpec mux_a = mux->getPort(ID::A); + RTLIL::SigSpec mux_b = mux->getPort(ID::B); RTLIL::SigSpec mux_s = mux->getPort(ID(S)); RTLIL::SigSpec shared_pmux_a = RTLIL::Const(RTLIL::State::Sx, max_width); @@ -216,24 +216,24 @@ void merge_operators(RTLIL::Module *module, RTLIL::Cell *mux, const std::vector< int conn_width = ports[0].sig.size(); int conn_offset = ports[0].mux_port_offset; - shared_op->setPort(ID(Y), shared_op->getPort(ID(Y)).extract(0, conn_width)); + shared_op->setPort(ID::Y, shared_op->getPort(ID::Y).extract(0, conn_width)); if (mux->type == ID($pmux)) { shared_pmux_s = RTLIL::SigSpec(); for (const auto &p : ports) { shared_pmux_s.append(mux_s[p.mux_port_id]); - mux_b.replace(p.mux_port_id * mux_a.size() + conn_offset, shared_op->getPort(ID(Y))); + mux_b.replace(p.mux_port_id * mux_a.size() + conn_offset, shared_op->getPort(ID::Y)); } } else { shared_pmux_s = RTLIL::SigSpec{mux_s, module->Not(NEW_ID, mux_s)}; - mux_a.replace(conn_offset, shared_op->getPort(ID(Y))); - mux_b.replace(conn_offset, shared_op->getPort(ID(Y))); + mux_a.replace(conn_offset, shared_op->getPort(ID::Y)); + mux_b.replace(conn_offset, shared_op->getPort(ID::Y)); } - mux->setPort(ID(A), mux_a); - mux->setPort(ID(B), mux_b); - mux->setPort(ID(Y), mux_y); + mux->setPort(ID::A, mux_a); + mux->setPort(ID::B, mux_b); + mux->setPort(ID::Y, mux_y); mux->setPort(ID(S), mux_s); for (const auto &op : muxed_operands) @@ -251,11 +251,11 @@ void merge_operators(RTLIL::Module *module, RTLIL::Cell *mux, const std::vector< shared_op->setParam(ID(Y_WIDTH), conn_width); - if (decode_port(shared_op, ID(A), &assign_map) == operand) { - shared_op->setPort(ID(B), mux_to_oper); + if (decode_port(shared_op, ID::A, &assign_map) == operand) { + shared_op->setPort(ID::B, mux_to_oper); shared_op->setParam(ID(B_WIDTH), max_width); } else { - shared_op->setPort(ID(A), mux_to_oper); + shared_op->setPort(ID::A, mux_to_oper); shared_op->setParam(ID(A_WIDTH), max_width); } } @@ -286,9 +286,9 @@ void check_muxed_operands(std::vector<const OpMuxConn *> &ports, const ExtSigSpe auto p = *it; auto op = p->op; - RTLIL::IdString muxed_port_name = ID(A); - if (decode_port(op, ID(A), &assign_map) == shared_operand) { - muxed_port_name = ID(B); + RTLIL::IdString muxed_port_name = ID::A; + if (decode_port(op, ID::A, &assign_map) == shared_operand) { + muxed_port_name = ID::B; } auto operand = decode_port(op, muxed_port_name, &assign_map); @@ -315,7 +315,7 @@ ExtSigSpec find_shared_operand(const OpMuxConn* seed, std::vector<const OpMuxCon auto op_a = seed->op; - for (RTLIL::IdString port_name : {ID(A), ID(B)}) { + for (RTLIL::IdString port_name : {ID::A, ID::B}) { oper = decode_port(op_a, port_name, &assign_map); auto operand_users = operand_to_users.at(oper); @@ -355,7 +355,7 @@ dict<RTLIL::SigSpec, OpMuxConn> find_valid_op_mux_conns(RTLIL::Module *module, d std::function<void(RTLIL::SigBit)> remove_outsig_from_aux_bit = [&](RTLIL::SigBit auxbit) { auto aux_outsig = op_aux_to_outsig.at(auxbit); auto op = outsig_to_operator.at(aux_outsig); - auto op_outsig = assign_map(op->getPort(ID(Y))); + auto op_outsig = assign_map(op->getPort(ID::Y)); remove_outsig(op_outsig); for (auto aux_outbit : aux_outsig) @@ -367,11 +367,11 @@ dict<RTLIL::SigSpec, OpMuxConn> find_valid_op_mux_conns(RTLIL::Module *module, d int mux_port_size; if (mux->type.in(ID($mux), ID($_MUX_))) { - mux_port_size = mux->getPort(ID(A)).size(); - sig = RTLIL::SigSpec{mux->getPort(ID(B)), mux->getPort(ID(A))}; + mux_port_size = mux->getPort(ID::A).size(); + sig = RTLIL::SigSpec{mux->getPort(ID::B), mux->getPort(ID::A)}; } else { - mux_port_size = mux->getPort(ID(A)).size(); - sig = mux->getPort(ID(B)); + mux_port_size = mux->getPort(ID::A).size(); + sig = mux->getPort(ID::B); } auto mux_insig = assign_map(sig); @@ -510,12 +510,12 @@ struct OptSharePass : public Pass { } } - auto mux_insig = assign_map(cell->getPort(ID(Y))); + auto mux_insig = assign_map(cell->getPort(ID::Y)); outsig_to_operator[mux_insig] = cell; for (auto outbit : mux_insig) op_outbit_to_outsig[outbit] = mux_insig; - for (RTLIL::IdString port_name : {ID(A), ID(B)}) { + for (RTLIL::IdString port_name : {ID::A, ID::B}) { auto op_insig = decode_port(cell, port_name, &assign_map); op_insigs.push_back(op_insig); operand_to_users[op_insig].insert(cell); diff --git a/passes/pmgen/.gitignore b/passes/pmgen/.gitignore index 0ad36ea2c..6b319b8c3 100644 --- a/passes/pmgen/.gitignore +++ b/passes/pmgen/.gitignore @@ -1,2 +1 @@ -/ice40_dsp_pm.h -/peepopt_pm.h +/*_pm.h
\ No newline at end of file diff --git a/passes/pmgen/Makefile.inc b/passes/pmgen/Makefile.inc index c03606152..8e0cbdca8 100644 --- a/passes/pmgen/Makefile.inc +++ b/passes/pmgen/Makefile.inc @@ -1,30 +1,29 @@ -OBJS += passes/pmgen/ice40_dsp.o -OBJS += passes/pmgen/ice40_wrapcarry.o -OBJS += passes/pmgen/peepopt.o +%_pm.h: passes/pmgen/pmgen.py %.pmg + $(P) mkdir -p passes/pmgen && python3 $< -o $@ -p $(subst _pm.h,,$(notdir $@)) $(filter-out $<,$^) # -------------------------------------- -passes/pmgen/ice40_dsp.o: passes/pmgen/ice40_dsp_pm.h -EXTRA_OBJS += passes/pmgen/ice40_dsp_pm.h -.SECONDARY: passes/pmgen/ice40_dsp_pm.h +OBJS += passes/pmgen/test_pmgen.o +passes/pmgen/test_pmgen.o: passes/pmgen/test_pmgen_pm.h passes/pmgen/ice40_dsp_pm.h passes/pmgen/peepopt_pm.h +$(eval $(call add_extra_objs,passes/pmgen/test_pmgen_pm.h)) -passes/pmgen/ice40_dsp_pm.h: passes/pmgen/pmgen.py passes/pmgen/ice40_dsp.pmg - $(P) mkdir -p passes/pmgen && python3 $< -o $@ -p ice40_dsp $(filter-out $<,$^) +# -------------------------------------- + +OBJS += passes/pmgen/ice40_dsp.o +passes/pmgen/ice40_dsp.o: passes/pmgen/ice40_dsp_pm.h +$(eval $(call add_extra_objs,passes/pmgen/ice40_dsp_pm.h)) # -------------------------------------- +OBJS += passes/pmgen/ice40_wrapcarry.o passes/pmgen/ice40_wrapcarry.o: passes/pmgen/ice40_wrapcarry_pm.h -EXTRA_OBJS += passes/pmgen/ice40_wrapcarry_pm.h -.SECONDARY: passes/pmgen/ice40_wrapcarry_pm.h - -passes/pmgen/ice40_wrapcarry_pm.h: passes/pmgen/pmgen.py passes/pmgen/ice40_wrapcarry.pmg - $(P) mkdir -p passes/pmgen && python3 $< -o $@ -p ice40_wrapcarry $(filter-out $<,$^) +$(eval $(call add_extra_objs,passes/pmgen/ice40_wrapcarry_pm.h)) # -------------------------------------- +OBJS += passes/pmgen/peepopt.o passes/pmgen/peepopt.o: passes/pmgen/peepopt_pm.h -EXTRA_OBJS += passes/pmgen/peepopt_pm.h -.SECONDARY: passes/pmgen/peepopt_pm.h +$(eval $(call add_extra_objs,passes/pmgen/peepopt_pm.h)) PEEPOPT_PATTERN = passes/pmgen/peepopt_shiftmul.pmg PEEPOPT_PATTERN += passes/pmgen/peepopt_muldiv.pmg diff --git a/passes/pmgen/README.md b/passes/pmgen/README.md index 2f0b1fd5a..5f6a8ab1b 100644 --- a/passes/pmgen/README.md +++ b/passes/pmgen/README.md @@ -45,9 +45,9 @@ of type `foobar_pm::state_<pattern_name>_t`.) Similarly the `.pmg` file declares user data variables that become members of `.ud_<pattern_name>`, a struct of type `foobar_pm::udata_<pattern_name>_t`. -There are four versions of the `run_<pattern_name>()` method: Without callback, -callback without arguments, callback with reference to `pm`, and callback with -reference to `pm.st_<pattern_name>`. +There are three versions of the `run_<pattern_name>()` method: Without callback, +callback without arguments, and callback with reference to `pm`. All versions +of the `run_<pattern_name>()` method return the number of found matches. The .pmg File Format @@ -118,8 +118,8 @@ write matchers: connected to any of the given signal bits, plus one if any of the signal bits is also a primary input or primary output. -- In `code..endcode` blocks there exist `accept`, `reject`, and `branch` - statements. +- In `code..endcode` blocks there exist `accept`, `reject`, `branch`, + `finish`, and `subpattern` statements. - In `index` statements there is a special `===` operator for the index lookup. @@ -175,6 +175,9 @@ explore the case where `mul` is set to `nullptr`. Without the `optional` statement a match may only be assigned nullptr when one of the `if` expressions evaluates to `false`. +The `semioptional` statement marks matches that must match if at least one +matching cell exists, but if no matching cell exists it is set to `nullptr`. + Additional code --------------- @@ -232,5 +235,108 @@ But in some cases it is more natural to utilize the implicit branch statement: portAB = \B; endcode -There is an implicit `code..endcode` block at the end of each `.pmg` file -that just accepts everything that gets all the way there. +There is an implicit `code..endcode` block at the end of each (sub)pattern +that just rejects. + +A `code..finally..endcode` block executes the code after `finally` during +back-tracking. This is useful for maintaining user data state or printing +debug messages. For example: + + udata <vector<Cell*>> stack + + code + stack.push_back(addAB); + ... + finally + stack.pop_back(); + endcode + +`accept` and `finish` statements can be used inside the `finally` section, +but not `reject`, `branch`, or `subpattern`. + +Declaring a subpattern +---------------------- + +A subpattern starts with a line containing the `subpattern` keyword followed +by the name of the subpattern. Subpatterns can be called from a `code` block +using a `subpattern(<subpattern_name>);` C statement. + +Arguments may be passed to subpattern via state variables. The `subpattern` +line must be followed by a `arg <arg1> <arg2> ...` line that lists the +state variables used to pass arguments. + + state <IdString> foobar_type + state <bool> foobar_state + + code foobar_type foobar_state + foobar_state = false; + foobar_type = $add; + subpattern(foo); + foobar_type = $sub; + subpattern(bar); + endcode + + subpattern foo + arg foobar_type foobar_state + + match addsub + index <IdString> addsub->type === foobar_type + ... + endmatch + + code + if (foobar_state) { + subpattern(tail); + } else { + foobar_state = true; + subpattern(bar); + } + endcode + + subpattern bar + arg foobar_type foobar_state + + match addsub + index <IdString> addsub->type === foobar_type + ... + endmatch + + code + if (foobar_state) { + subpattern(tail); + } else { + foobar_state = true; + subpattern(foo); + } + endcode + + subpattern tail + ... + +Subpatterns cann be called recursively. + +If a `subpattern` statement is preceded by a `fallthrough` statement, this is +equivalent to calling the subpattern at the end of the preceding block. + +Generate Blocks +--------------- + +Match blocks may contain an optional `generate` section that is used for automatic +test-case generation. For example: + + match mul + ... + generate 10 + SigSpec Y = port(ff, \D); + SigSpec A = module->addWire(NEW_ID, GetSize(Y) - rng(GetSize(Y)/2)); + SigSpec B = module->addWire(NEW_ID, GetSize(Y) - rng(GetSize(Y)/2)); + module->addMul(NEW_ID, A, B, Y, rng(2)); + endmatch + +The expression `rng(n)` returns a non-negative integer less than `n`. + +The argument to `generate` is the chance of this generate block being executed +when the match block did not match anything, in percent. + +The special statement `finish` can be used within generate blocks to terminate +the current pattern matcher run. diff --git a/passes/pmgen/ice40_dsp.pmg b/passes/pmgen/ice40_dsp.pmg index 1f3590d4e..7003092bb 100644 --- a/passes/pmgen/ice40_dsp.pmg +++ b/passes/pmgen/ice40_dsp.pmg @@ -159,4 +159,5 @@ code clock clock_pol clock_vld clock_pol = cp; clock_vld = true; } + accept; endcode diff --git a/passes/pmgen/peepopt_muldiv.pmg b/passes/pmgen/peepopt_muldiv.pmg index 06c275834..7cad759d0 100644 --- a/passes/pmgen/peepopt_muldiv.pmg +++ b/passes/pmgen/peepopt_muldiv.pmg @@ -32,5 +32,5 @@ code log("muldiv pattern in %s: mul=%s, div=%s\n", log_id(module), log_id(mul), log_id(div)); module->connect(div_y, val_y); autoremove(div); - reject; + accept; endcode diff --git a/passes/pmgen/peepopt_shiftmul.pmg b/passes/pmgen/peepopt_shiftmul.pmg index 6adab4e5f..d4748ae19 100644 --- a/passes/pmgen/peepopt_shiftmul.pmg +++ b/passes/pmgen/peepopt_shiftmul.pmg @@ -34,6 +34,7 @@ match mul endmatch code +{ IdString const_factor_port = port(mul, \A).is_fully_const() ? \A : \B; IdString const_factor_signed = const_factor_port == \A ? \A_SIGNED : \B_SIGNED; Const const_factor_cnst = port(mul, const_factor_port).as_const(); @@ -90,5 +91,6 @@ code shift->setParam(\B_WIDTH, GetSize(new_b)); blacklist(shift); - reject; + accept; +} endcode diff --git a/passes/pmgen/pmgen.py b/passes/pmgen/pmgen.py index 81052afce..18c3bf5a5 100644 --- a/passes/pmgen/pmgen.py +++ b/passes/pmgen/pmgen.py @@ -38,7 +38,10 @@ for a in args: assert prefix is not None current_pattern = None +current_subpattern = None patterns = dict() +subpatterns = dict() +subpattern_args = dict() state_types = dict() udata_types = dict() blocks = list() @@ -104,9 +107,12 @@ def rewrite_cpp(s): return "".join(t) -def process_pmgfile(f): +def process_pmgfile(f, filename): + linenr = 0 global current_pattern + global current_subpattern while True: + linenr += 1 line = f.readline() if line == "": break line = line.strip() @@ -119,19 +125,52 @@ def process_pmgfile(f): if current_pattern is not None: block = dict() block["type"] = "final" - block["pattern"] = current_pattern + block["pattern"] = (current_pattern, current_subpattern) blocks.append(block) line = line.split() assert len(line) == 2 assert line[1] not in patterns current_pattern = line[1] + current_subpattern = "" patterns[current_pattern] = len(blocks) + subpatterns[(current_pattern, current_subpattern)] = len(blocks) + subpattern_args[(current_pattern, current_subpattern)] = list() state_types[current_pattern] = dict() udata_types[current_pattern] = dict() continue assert current_pattern is not None + if cmd == "fallthrough": + block = dict() + block["type"] = "fallthrough" + blocks.append(block) + line = line.split() + assert len(line) == 1 + continue + + if cmd == "subpattern": + if len(blocks) == 0 or blocks[-1]["type"] != "fallthrough": + block = dict() + block["type"] = "final" + block["pattern"] = (current_pattern, current_subpattern) + blocks.append(block) + elif len(blocks) and blocks[-1]["type"] == "fallthrough": + del blocks[-1] + line = line.split() + assert len(line) == 2 + current_subpattern = line[1] + subpattern_args[(current_pattern, current_subpattern)] = list() + assert (current_pattern, current_subpattern) not in subpatterns + subpatterns[(current_pattern, current_subpattern)] = len(blocks) + continue + + if cmd == "arg": + line = line.split() + assert len(line) > 1 + subpattern_args[(current_pattern, current_subpattern)] += line[1:] + continue + if cmd == "state": m = re.match(r"^state\s+<(.*?)>\s+(([A-Za-z_][A-Za-z_0-9]*\s+)*[A-Za-z_][A-Za-z_0-9]*)\s*$", line) assert m @@ -155,11 +194,15 @@ def process_pmgfile(f): if cmd == "match": block = dict() block["type"] = "match" - block["pattern"] = current_pattern + block["src"] = "%s:%d" % (filename, linenr) + block["pattern"] = (current_pattern, current_subpattern) + + block["genargs"] = None + block["gencode"] = None line = line.split() assert len(line) == 2 - assert line[1] not in state_types[current_pattern] + assert (line[1] not in state_types[current_pattern]) or (state_types[current_pattern][line[1]] == "Cell*") block["cell"] = line[1] state_types[current_pattern][line[1]] = "Cell*"; @@ -168,8 +211,10 @@ def process_pmgfile(f): block["index"] = list() block["filter"] = list() block["optional"] = False + block["semioptional"] = False while True: + linenr += 1 l = f.readline() assert l != "" a = l.split() @@ -201,31 +246,60 @@ def process_pmgfile(f): block["optional"] = True continue + if a[0] == "semioptional": + block["semioptional"] = True + continue + + if a[0] == "generate": + block["genargs"] = list([int(s) for s in a[1:]]) + block["gencode"] = list() + assert len(block["genargs"]) < 2 + while True: + linenr += 1 + l = f.readline() + assert l != "" + a = l.split() + if a[0] == "endmatch": break + block["gencode"].append(rewrite_cpp(l.rstrip())) + break + assert False + if block["optional"]: + assert not block["semioptional"] + blocks.append(block) continue if cmd == "code": block = dict() block["type"] = "code" - block["pattern"] = current_pattern + block["src"] = "%s:%d" % (filename, linenr) + block["pattern"] = (current_pattern, current_subpattern) block["code"] = list() + block["fcode"] = list() block["states"] = set() for s in line.split()[1:]: assert s in state_types[current_pattern] block["states"].add(s) + codetype = "code" + while True: + linenr += 1 l = f.readline() assert l != "" a = l.split() if len(a) == 0: continue if a[0] == "endcode": break - block["code"].append(rewrite_cpp(l.rstrip())) + if a[0] == "finally": + codetype = "fcode" + continue + + block[codetype].append(rewrite_cpp(l.rstrip())) blocks.append(block) continue @@ -234,15 +308,16 @@ def process_pmgfile(f): for fn in pmgfiles: with open(fn, "r") as f: - process_pmgfile(f) + process_pmgfile(f, fn) if current_pattern is not None: block = dict() block["type"] = "final" - block["pattern"] = current_pattern + block["pattern"] = (current_pattern, current_subpattern) blocks.append(block) current_pattern = None +current_subpattern = None if debug: pp.pprint(blocks) @@ -262,7 +337,18 @@ with open(outfile, "w") as f: print("struct {}_pm {{".format(prefix), file=f) print(" Module *module;", file=f) print(" SigMap sigmap;", file=f) - print(" std::function<void()> on_accept;".format(prefix), file=f) + print(" std::function<void()> on_accept;", file=f) + print(" bool generate_mode;", file=f) + print(" int accept_cnt;", file=f) + print("", file=f) + + print(" uint32_t rngseed;", file=f) + print(" int rng(unsigned int n) {", file=f) + print(" rngseed ^= rngseed << 13;", file=f) + print(" rngseed ^= rngseed >> 17;", file=f) + print(" rngseed ^= rngseed << 5;", file=f) + print(" return rngseed % n;", file=f) + print(" }", file=f) print("", file=f) for index in range(len(blocks)): @@ -276,7 +362,7 @@ with open(outfile, "w") as f: print(" dict<SigBit, pool<Cell*>> sigusers;", file=f) print(" pool<Cell*> blacklist_cells;", file=f) print(" pool<Cell*> autoremove_cells;", file=f) - print(" bool blacklist_dirty;", file=f) + print(" dict<Cell*,int> rollback_cache;", file=f) print(" int rollback;", file=f) print("", file=f) @@ -312,39 +398,24 @@ with open(outfile, "w") as f: print("", file=f) print(" void blacklist(Cell *cell) {", file=f) - print(" if (cell != nullptr) {", file=f) - print(" if (blacklist_cells.insert(cell).second)", file=f) - print(" blacklist_dirty = true;", file=f) + print(" if (cell != nullptr && blacklist_cells.insert(cell).second) {", file=f) + print(" auto ptr = rollback_cache.find(cell);", file=f) + print(" if (ptr == rollback_cache.end()) return;", file=f) + print(" int rb = ptr->second;", file=f) + print(" if (rollback == 0 || rollback > rb)", file=f) + print(" rollback = rb;", file=f) print(" }", file=f) print(" }", file=f) print("", file=f) print(" void autoremove(Cell *cell) {", file=f) print(" if (cell != nullptr) {", file=f) - print(" if (blacklist_cells.insert(cell).second)", file=f) - print(" blacklist_dirty = true;", file=f) print(" autoremove_cells.insert(cell);", file=f) + print(" blacklist(cell);", file=f) print(" }", file=f) print(" }", file=f) print("", file=f) - for current_pattern in sorted(patterns.keys()): - print(" void check_blacklist_{}() {{".format(current_pattern), file=f) - print(" if (!blacklist_dirty)", file=f) - print(" return;", file=f) - print(" blacklist_dirty = false;", file=f) - for index in range(len(blocks)): - block = blocks[index] - if block["pattern"] != current_pattern: - continue - if block["type"] == "match": - print(" if (st_{}.{} != nullptr && blacklist_cells.count(st_{}.{})) {{".format(current_pattern, block["cell"], current_pattern, block["cell"]), file=f) - print(" rollback = {};".format(index+1), file=f) - print(" return;", file=f) - print(" }", file=f) - print(" rollback = 0;", file=f) - print(" }", file=f) - print("", file=f) current_pattern = None print(" SigSpec port(Cell *cell, IdString portname) {", file=f) @@ -367,7 +438,7 @@ with open(outfile, "w") as f: print("", file=f) print(" {}_pm(Module *module, const vector<Cell*> &cells) :".format(prefix), file=f) - print(" module(module), sigmap(module) {", file=f) + print(" module(module), sigmap(module), generate_mode(false), rngseed(12345678) {", file=f) for current_pattern in sorted(patterns.keys()): for s, t in sorted(udata_types[current_pattern].items()): if t.endswith("*"): @@ -405,41 +476,47 @@ with open(outfile, "w") as f: print("", file=f) for current_pattern in sorted(patterns.keys()): - print(" void run_{}(std::function<void()> on_accept_f) {{".format(current_pattern), file=f) + print(" int run_{}(std::function<void()> on_accept_f) {{".format(current_pattern), file=f) + print(" accept_cnt = 0;", file=f) print(" on_accept = on_accept_f;", file=f) print(" rollback = 0;", file=f) - print(" blacklist_dirty = false;", file=f) for s, t in sorted(state_types[current_pattern].items()): if t.endswith("*"): print(" st_{}.{} = nullptr;".format(current_pattern, s), file=f) else: print(" st_{}.{} = {}();".format(current_pattern, s, t), file=f) - print(" block_{}();".format(patterns[current_pattern]), file=f) + print(" block_{}(1);".format(patterns[current_pattern]), file=f) + print(" log_assert(rollback_cache.empty());", file=f) + print(" return accept_cnt;", file=f) print(" }", file=f) print("", file=f) - print(" void run_{}(std::function<void({}_pm&)> on_accept_f) {{".format(current_pattern, prefix), file=f) - print(" run_{}([&](){{on_accept_f(*this);}});".format(current_pattern), file=f) + print(" int run_{}(std::function<void({}_pm&)> on_accept_f) {{".format(current_pattern, prefix), file=f) + print(" return run_{}([&](){{on_accept_f(*this);}});".format(current_pattern), file=f) print(" }", file=f) print("", file=f) - print(" void run_{}(std::function<void(state_{}_t&)> on_accept_f) {{".format(current_pattern, current_pattern), file=f) - print(" run_{}([&](){{on_accept_f(st_{});}});".format(current_pattern, current_pattern), file=f) + print(" int run_{}() {{".format(current_pattern), file=f) + print(" return run_{}([](){{}});".format(current_pattern, current_pattern), file=f) print(" }", file=f) print("", file=f) - print(" void run_{}() {{".format(current_pattern), file=f) - print(" run_{}([](){{}});".format(current_pattern, current_pattern), file=f) - print(" }", file=f) + + if len(subpatterns): + for p, s in sorted(subpatterns.keys()): + print(" void block_subpattern_{}_{}(int recursion) {{ block_{}(recursion); }}".format(p, s, subpatterns[(p, s)]), file=f) print("", file=f) + current_pattern = None + current_subpattern = None for index in range(len(blocks)): block = blocks[index] - print(" void block_{}() {{".format(index), file=f) - current_pattern = block["pattern"] + if block["type"] in ("match", "code"): + print(" // {}".format(block["src"]), file=f) + + print(" void block_{}(int recursion YS_ATTRIBUTE(unused)) {{".format(index), file=f) + current_pattern, current_subpattern = block["pattern"] if block["type"] == "final": - print(" on_accept();", file=f) - print(" check_blacklist_{}();".format(current_pattern), file=f) print(" }", file=f) if index+1 != len(blocks): print("", file=f) @@ -449,7 +526,10 @@ with open(outfile, "w") as f: nonconst_st = set() restore_st = set() - for i in range(patterns[current_pattern], index): + for s in subpattern_args[(current_pattern, current_subpattern)]: + const_st.add(s) + + for i in range(subpatterns[(current_pattern, current_subpattern)], index): if blocks[i]["type"] == "code": for s in blocks[i]["states"]: const_st.add(s) @@ -482,6 +562,10 @@ with open(outfile, "w") as f: t = state_types[current_pattern][s] print(" {} &{} YS_ATTRIBUTE(unused) = st_{}.{};".format(t, s, current_pattern, s), file=f) + for u in sorted(udata_types[current_pattern].keys()): + t = udata_types[current_pattern][u] + print(" {} &{} YS_ATTRIBUTE(unused) = ud_{}.{};".format(t, u, current_pattern, u), file=f) + if len(restore_st): print("", file=f) for s in sorted(restore_st): @@ -490,24 +574,38 @@ with open(outfile, "w") as f: if block["type"] == "code": print("", file=f) - print(" do {", file=f) - print("#define reject do {{ check_blacklist_{}(); goto rollback_label; }} while(0)".format(current_pattern), file=f) - print("#define accept do {{ on_accept(); check_blacklist_{}(); if (rollback) goto rollback_label; }} while(0)".format(current_pattern), file=f) - print("#define branch do {{ block_{}(); if (rollback) goto rollback_label; }} while(0)".format(index+1), file=f) + print("#define reject do { goto rollback_label; } while(0)", file=f) + print("#define accept do { accept_cnt++; on_accept(); if (rollback) goto rollback_label; } while(0)", file=f) + print("#define finish do { rollback = -1; goto rollback_label; } while(0)", file=f) + print("#define branch do {{ block_{}(recursion+1); if (rollback) goto rollback_label; }} while(0)".format(index+1), file=f) + print("#define subpattern(pattern_name) do {{ block_subpattern_{}_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; }} while(0)".format(current_pattern), file=f) for line in block["code"]: - print(" " + line, file=f) + print(" " + line, file=f) print("", file=f) - print(" block_{}();".format(index+1), file=f) + print(" block_{}(recursion+1);".format(index+1), file=f) + print("#undef reject", file=f) print("#undef accept", file=f) + print("#undef finish", file=f) print("#undef branch", file=f) - print(" } while (0);", file=f) + print("#undef subpattern", file=f) + print("", file=f) print("rollback_label:", file=f) print(" YS_ATTRIBUTE(unused);", file=f) + if len(block["fcode"]): + print("#define accept do { accept_cnt++; on_accept(); } while(0)", file=f) + print("#define finish do { rollback = -1; goto finish_label; } while(0)", file=f) + for line in block["fcode"]: + print(" " + line, file=f) + print("finish_label:", file=f) + print(" YS_ATTRIBUTE(unused);", file=f) + print("#undef accept", file=f) + print("#undef finish", file=f) + if len(restore_st) or len(nonconst_st): print("", file=f) for s in sorted(restore_st): @@ -524,12 +622,15 @@ with open(outfile, "w") as f: elif block["type"] == "match": assert len(restore_st) == 0 + print(" Cell* backup_{} = {};".format(block["cell"], block["cell"]), file=f) + if len(block["if"]): for expr in block["if"]: print("", file=f) print(" if (!({})) {{".format(expr), file=f) print(" {} = nullptr;".format(block["cell"]), file=f) - print(" block_{}();".format(index+1), file=f) + print(" block_{}(recursion+1);".format(index+1), file=f) + print(" {} = backup_{};".format(block["cell"], block["cell"]), file=f) print(" return;", file=f) print(" }", file=f) @@ -537,21 +638,32 @@ with open(outfile, "w") as f: print(" index_{}_key_type key;".format(index), file=f) for field, entry in enumerate(block["index"]): print(" std::get<{}>(key) = {};".format(field, entry[2]), file=f) - print(" const vector<Cell*> &cells = index_{}[key];".format(index), file=f) + print(" auto cells_ptr = index_{}.find(key);".format(index), file=f) + + if block["semioptional"] or block["genargs"] is not None: + print(" bool found_any_match = false;", file=f) print("", file=f) - print(" for (int idx = 0; idx < GetSize(cells); idx++) {", file=f) - print(" {} = cells[idx];".format(block["cell"]), file=f) - print(" if (blacklist_cells.count({})) continue;".format(block["cell"]), file=f) + print(" if (cells_ptr != index_{}.end()) {{".format(index), file=f) + print(" const vector<Cell*> &cells = cells_ptr->second;".format(index), file=f) + print(" for (int idx = 0; idx < GetSize(cells); idx++) {", file=f) + print(" {} = cells[idx];".format(block["cell"]), file=f) + print(" if (blacklist_cells.count({})) continue;".format(block["cell"]), file=f) for expr in block["filter"]: - print(" if (!({})) continue;".format(expr), file=f) - print(" block_{}();".format(index+1), file=f) - print(" if (rollback) {", file=f) - print(" if (rollback != {}) {{".format(index+1), file=f) - print(" {} = nullptr;".format(block["cell"]), file=f) - print(" return;", file=f) + print(" if (!({})) continue;".format(expr), file=f) + if block["semioptional"] or block["genargs"] is not None: + print(" found_any_match = true;", file=f) + print(" auto rollback_ptr = rollback_cache.insert(make_pair(cells[idx], recursion));", file=f) + print(" block_{}(recursion+1);".format(index+1), file=f) + print(" if (rollback_ptr.second)", file=f) + print(" rollback_cache.erase(rollback_ptr.first);", file=f) + print(" if (rollback) {", file=f) + print(" if (rollback != recursion) {{".format(index+1), file=f) + print(" {} = backup_{};".format(block["cell"], block["cell"]), file=f) + print(" return;", file=f) + print(" }", file=f) + print(" rollback = 0;", file=f) print(" }", file=f) - print(" rollback = 0;", file=f) print(" }", file=f) print(" }", file=f) @@ -559,8 +671,22 @@ with open(outfile, "w") as f: print(" {} = nullptr;".format(block["cell"]), file=f) if block["optional"]: - print(" block_{}();".format(index+1), file=f) + print(" block_{}(recursion+1);".format(index+1), file=f) + if block["semioptional"]: + print(" if (!found_any_match) block_{}(recursion+1);".format(index+1), file=f) + + print(" {} = backup_{};".format(block["cell"], block["cell"]), file=f) + + if block["genargs"] is not None: + print("#define finish do { rollback = -1; return; } while(0)", file=f) + print(" if (generate_mode && !found_any_match) {", file=f) + if len(block["genargs"]) == 1: + print(" if (rng(100) >= {}) return;".format(block["genargs"][0]), file=f) + for line in block["gencode"]: + print(" " + line, file=f) + print(" }", file=f) + print("#undef finish", file=f) else: assert False diff --git a/passes/pmgen/test_pmgen.cc b/passes/pmgen/test_pmgen.cc new file mode 100644 index 000000000..9f42a95d0 --- /dev/null +++ b/passes/pmgen/test_pmgen.cc @@ -0,0 +1,330 @@ +/* + * 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 + +// for peepopt_pm +bool did_something; + +#include "passes/pmgen/test_pmgen_pm.h" +#include "passes/pmgen/ice40_dsp_pm.h" +#include "passes/pmgen/peepopt_pm.h" + +void reduce_chain(test_pmgen_pm &pm) +{ + auto &st = pm.st_reduce; + auto &ud = pm.ud_reduce; + + if (ud.longest_chain.empty()) + return; + + log("Found chain of length %d (%s):\n", GetSize(ud.longest_chain), log_id(st.first->type)); + + SigSpec A; + SigSpec Y = ud.longest_chain.front().first->getPort(ID(Y)); + auto last_cell = ud.longest_chain.back().first; + + for (auto it : ud.longest_chain) { + auto cell = it.first; + if (cell == last_cell) { + A.append(cell->getPort(ID(A))); + A.append(cell->getPort(ID(B))); + } else { + A.append(cell->getPort(it.second == ID(A) ? ID(B) : ID(A))); + } + log(" %s\n", log_id(cell)); + pm.autoremove(cell); + } + + Cell *c; + + if (last_cell->type == ID($_AND_)) + c = pm.module->addReduceAnd(NEW_ID, A, Y); + else if (last_cell->type == ID($_OR_)) + c = pm.module->addReduceOr(NEW_ID, A, Y); + else if (last_cell->type == ID($_XOR_)) + c = pm.module->addReduceXor(NEW_ID, A, Y); + else + log_abort(); + + log(" -> %s (%s)\n", log_id(c), log_id(c->type)); +} + +void reduce_tree(test_pmgen_pm &pm) +{ + auto &st = pm.st_reduce; + auto &ud = pm.ud_reduce; + + if (ud.longest_chain.empty()) + return; + + SigSpec A = ud.leaves; + SigSpec Y = st.first->getPort(ID(Y)); + pm.autoremove(st.first); + + log("Found %s tree with %d leaves for %s (%s).\n", log_id(st.first->type), + GetSize(A), log_signal(Y), log_id(st.first)); + + Cell *c; + + if (st.first->type == ID($_AND_)) + c = pm.module->addReduceAnd(NEW_ID, A, Y); + else if (st.first->type == ID($_OR_)) + c = pm.module->addReduceOr(NEW_ID, A, Y); + else if (st.first->type == ID($_XOR_)) + c = pm.module->addReduceXor(NEW_ID, A, Y); + else + log_abort(); + + log(" -> %s (%s)\n", log_id(c), log_id(c->type)); +} + +#define GENERATE_PATTERN(pmclass, pattern) \ + generate_pattern<pmclass>([](pmclass &pm, std::function<void()> f){ return pm.run_ ## pattern(f); }, #pmclass, #pattern, design) + +void pmtest_addports(Module *module) +{ + pool<SigBit> driven_bits, used_bits; + SigMap sigmap(module); + int icnt = 0, ocnt = 0; + + for (auto cell : module->cells()) + for (auto conn : cell->connections()) + { + if (cell->input(conn.first)) + for (auto bit : sigmap(conn.second)) + used_bits.insert(bit); + if (cell->output(conn.first)) + for (auto bit : sigmap(conn.second)) + driven_bits.insert(bit); + } + + for (auto wire : vector<Wire*>(module->wires())) + { + SigSpec ibits, obits; + for (auto bit : sigmap(wire)) { + if (!used_bits.count(bit)) + obits.append(bit); + if (!driven_bits.count(bit)) + ibits.append(bit); + } + if (!ibits.empty()) { + Wire *w = module->addWire(stringf("\\i%d", icnt++), GetSize(ibits)); + w->port_input = true; + module->connect(ibits, w); + } + if (!obits.empty()) { + Wire *w = module->addWire(stringf("\\o%d", ocnt++), GetSize(obits)); + w->port_output = true; + module->connect(w, obits); + } + } + + module->fixup_ports(); +} + +template <class pm> +void generate_pattern(std::function<void(pm&,std::function<void()>)> run, const char *pmclass, const char *pattern, Design *design) +{ + log("Generating \"%s\" patterns for pattern matcher \"%s\".\n", pattern, pmclass); + + int modcnt = 0; + int maxsubcnt = 4; + int timeout = 0; + vector<Module*> mods; + + while (modcnt < 100) + { + int submodcnt = 0, itercnt = 0, cellcnt = 0; + Module *mod = design->addModule(NEW_ID); + + while (modcnt < 100 && submodcnt < maxsubcnt && itercnt++ < 1000) + { + if (timeout++ > 10000) + log_error("pmgen generator is stuck: 10000 iterations an no matching module generated.\n"); + + pm matcher(mod, mod->cells()); + + matcher.rng(1); + matcher.rngseed += modcnt; + matcher.rng(1); + matcher.rngseed += submodcnt; + matcher.rng(1); + matcher.rngseed += itercnt; + matcher.rng(1); + matcher.rngseed += cellcnt; + matcher.rng(1); + + if (GetSize(mod->cells()) != cellcnt) + { + bool found_match = false; + run(matcher, [&](){ found_match = true; }); + cellcnt = GetSize(mod->cells()); + + if (found_match) { + Module *m = design->addModule(stringf("\\pmtest_%s_%s_%05d", + pmclass, pattern, modcnt++)); + log("Creating module %s with %d cells.\n", log_id(m), cellcnt); + mod->cloneInto(m); + pmtest_addports(m); + mods.push_back(m); + submodcnt++; + timeout = 0; + } + } + + matcher.generate_mode = true; + run(matcher, [](){}); + } + + if (submodcnt) + maxsubcnt *= 2; + + design->remove(mod); + } + + Module *m = design->addModule(stringf("\\pmtest_%s_%s", pmclass, pattern)); + log("Creating module %s with %d cells.\n", log_id(m), GetSize(mods)); + for (auto mod : mods) { + Cell *c = m->addCell(mod->name, mod->name); + for (auto port : mod->ports) { + Wire *w = m->addWire(NEW_ID, GetSize(mod->wire(port))); + c->setPort(port, w); + } + } + pmtest_addports(m); +} + +struct TestPmgenPass : public Pass { + TestPmgenPass() : Pass("test_pmgen", "test pass for pmgen") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" test_pmgen -reduce_chain [options] [selection]\n"); + log("\n"); + log("Demo for recursive pmgen patterns. Map chains of AND/OR/XOR to $reduce_*.\n"); + log("\n"); + + log("\n"); + log(" test_pmgen -reduce_tree [options] [selection]\n"); + log("\n"); + log("Demo for recursive pmgen patterns. Map trees of AND/OR/XOR to $reduce_*.\n"); + log("\n"); + + log("\n"); + log(" test_pmgen -generate [options] <pattern_name>\n"); + log("\n"); + log("Create modules that match the specified pattern.\n"); + log("\n"); + } + + void execute_reduce_chain(std::vector<std::string> args, RTLIL::Design *design) + { + log_header(design, "Executing TEST_PMGEN pass (-reduce_chain).\n"); + + size_t argidx; + for (argidx = 2; argidx < args.size(); argidx++) + { + // if (args[argidx] == "-singleton") { + // singleton_mode = true; + // continue; + // } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + while (test_pmgen_pm(module, module->selected_cells()).run_reduce(reduce_chain)) {} + } + + void execute_reduce_tree(std::vector<std::string> args, RTLIL::Design *design) + { + log_header(design, "Executing TEST_PMGEN pass (-reduce_tree).\n"); + + size_t argidx; + for (argidx = 2; argidx < args.size(); argidx++) + { + // if (args[argidx] == "-singleton") { + // singleton_mode = true; + // continue; + // } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + test_pmgen_pm(module, module->selected_cells()).run_reduce(reduce_tree); + } + + void execute_generate(std::vector<std::string> args, RTLIL::Design *design) + { + log_header(design, "Executing TEST_PMGEN pass (-generate).\n"); + + size_t argidx; + for (argidx = 2; argidx < args.size(); argidx++) + { + // if (args[argidx] == "-singleton") { + // singleton_mode = true; + // continue; + // } + break; + } + + if (argidx+1 != args.size()) + log_cmd_error("Expected exactly one pattern.\n"); + + string pattern = args[argidx]; + + if (pattern == "reduce") + return GENERATE_PATTERN(test_pmgen_pm, reduce); + + if (pattern == "ice40_dsp") + return GENERATE_PATTERN(ice40_dsp_pm, ice40_dsp); + + if (pattern == "peepopt-muldiv") + return GENERATE_PATTERN(peepopt_pm, muldiv); + + if (pattern == "peepopt-shiftmul") + return GENERATE_PATTERN(peepopt_pm, shiftmul); + + log_cmd_error("Unkown pattern: %s\n", pattern.c_str()); + } + + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + if (GetSize(args) > 1) + { + if (args[1] == "-reduce_chain") + return execute_reduce_chain(args, design); + if (args[1] == "-reduce_tree") + return execute_reduce_tree(args, design); + if (args[1] == "-generate") + return execute_generate(args, design); + } + help(); + log_cmd_error("Missing or unsupported mode parameter.\n"); + } +} TestPmgenPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/pmgen/test_pmgen.pmg b/passes/pmgen/test_pmgen.pmg new file mode 100644 index 000000000..211477a62 --- /dev/null +++ b/passes/pmgen/test_pmgen.pmg @@ -0,0 +1,106 @@ +pattern reduce + +state <IdString> portname +udata <vector<pair<Cell*, IdString>>> chain longest_chain +udata <pool<Cell*>> non_first_cells +udata <SigSpec> leaves + +code + non_first_cells.clear(); + subpattern(setup); +endcode + +match first + select first->type.in($_AND_, $_OR_, $_XOR_) + filter !non_first_cells.count(first) +generate + SigSpec A = module->addWire(NEW_ID); + SigSpec B = module->addWire(NEW_ID); + SigSpec Y = module->addWire(NEW_ID); + switch (rng(3)) + { + case 0: + module->addAndGate(NEW_ID, A, B, Y); + break; + case 1: + module->addOrGate(NEW_ID, A, B, Y); + break; + case 2: + module->addXorGate(NEW_ID, A, B, Y); + break; + } +endmatch + +code + leaves = SigSpec(); + longest_chain.clear(); + chain.push_back(make_pair(first, \A)); + subpattern(tail); + chain.back().second = \B; + subpattern(tail); +finally + chain.pop_back(); + log_assert(chain.empty()); + if (GetSize(longest_chain) > 1) + accept; +endcode + +// ------------------------------------------------------------------ + +subpattern setup + +match first + select first->type.in($_AND_, $_OR_, $_XOR_) +endmatch + +code portname + portname = \A; + branch; + portname = \B; +endcode + +match next + select nusers(port(next, \Y)) == 2 + select next->type.in($_AND_, $_OR_, $_XOR_) + index <IdString> next->type === first->type + index <SigSpec> port(next, \Y) === port(first, portname) +endmatch + +code + non_first_cells.insert(next); +endcode + +// ------------------------------------------------------------------ + +subpattern tail +arg first + +match next + semioptional + select nusers(port(next, \Y)) == 2 + select next->type.in($_AND_, $_OR_, $_XOR_) + index <IdString> next->type === chain.back().first->type + index <SigSpec> port(next, \Y) === port(chain.back().first, chain.back().second) +generate 10 + SigSpec A = module->addWire(NEW_ID); + SigSpec B = module->addWire(NEW_ID); + SigSpec Y = port(chain.back().first, chain.back().second); + Cell *c = module->addAndGate(NEW_ID, A, B, Y); + c->type = chain.back().first->type; +endmatch + +code + if (next) { + chain.push_back(make_pair(next, \A)); + subpattern(tail); + chain.back().second = \B; + subpattern(tail); + } else { + if (GetSize(chain) > GetSize(longest_chain)) + longest_chain = chain; + leaves.append(port(chain.back().first, chain.back().second)); + } +finally + if (next) + chain.pop_back(); +endcode diff --git a/passes/proc/proc_clean.cc b/passes/proc/proc_clean.cc index 97f4c6573..114c6ab03 100644 --- a/passes/proc/proc_clean.cc +++ b/passes/proc/proc_clean.cc @@ -69,8 +69,7 @@ void proc_clean_switch(RTLIL::SwitchRule *sw, RTLIL::CaseRule *parent, bool &did did_something = true; for (auto &action : sw->cases[0]->actions) parent->actions.push_back(action); - for (auto sw2 : sw->cases[0]->switches) - parent->switches.push_back(sw2); + parent->switches.insert(parent->switches.begin(), sw->cases[0]->switches.begin(), sw->cases[0]->switches.end()); sw->cases[0]->switches.clear(); delete sw->cases[0]; sw->cases.clear(); diff --git a/passes/techmap/abc9.cc b/passes/techmap/abc9.cc index c1d4fff2d..84cb2c04f 100644 --- a/passes/techmap/abc9.cc +++ b/passes/techmap/abc9.cc @@ -76,12 +76,11 @@ 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) +void handle_loops(RTLIL::Design *design, + const dict<IdString,pool<IdString>> &scc_break_inputs) { Pass::call(design, "scc -set_attr abc_scc_id {}"); - dict<IdString, vector<IdString>> abc_scc_break; - // For every unique SCC found, (arbitrarily) find the first // cell in the component, and select (and mark) all its output // wires @@ -116,44 +115,29 @@ void handle_loops(RTLIL::Design *design) cell->attributes.erase(it); } - auto jt = abc_scc_break.find(cell->type); - if (jt == abc_scc_break.end()) { - std::vector<IdString> ports; - RTLIL::Module* box_module = design->module(cell->type); - if (box_module) { - auto ports_csv = box_module->attributes.at(ID(abc_scc_break), RTLIL::Const::from_string("")).decode_string(); - for (const auto &port_name : split_tokens(ports_csv, ",")) { - auto port_id = RTLIL::escape_id(port_name); - auto kt = cell->connections_.find(port_id); - if (kt == cell->connections_.end()) - log_error("abc_scc_break attribute value '%s' does not exist as port on module '%s'\n", port_name.c_str(), log_id(box_module)); - ports.push_back(port_id); - } - } - jt = abc_scc_break.insert(std::make_pair(cell->type, std::move(ports))).first; - } - - 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); + 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)); } - sig.append(RTLIL::SigBit(w, b.offset)); + rhs = sig; } - rhs = sig; - } } module->fixup_ports(); @@ -288,7 +272,9 @@ 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) + std::string wire_delay, const dict<int,IdString> &box_lookup, + const dict<IdString,pool<IdString>> &scc_break_inputs +) { module = current_module; map_autoidx = autoidx++; @@ -427,7 +413,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); + handle_loops(design, scc_break_inputs); Pass::call(design, "aigmap"); @@ -1081,6 +1067,7 @@ struct Abc9Pass : public Pass { extra_args(args, argidx, design); 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()) @@ -1093,6 +1080,56 @@ struct Abc9Pass : public Pass { log_error("Module '%s' has the same abc_box_id = %d value as '%s'.\n", log_id(m), id, log_id(r.first->second)); log_assert(r.second); + + RTLIL::Wire *carry_in = nullptr, *carry_out = nullptr; + 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 (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))) { + if (carry_out) + log_error("Module '%s' contains more than one 'abc_carry' input port.\n", log_id(m)); + carry_out = w; + } + } + } + if (carry_in || carry_out) { + if (carry_in && !carry_out) + log_error("Module '%s' contains an 'abc_carry' input port but no output port.\n", log_id(m)); + if (!carry_in && carry_out) + log_error("Module '%s' contains an 'abc_carry' output port but no input port.\n", log_id(m)); + // Make carry_in the last PI, and carry_out the last PO + // since ABC requires it this way + auto &ports = m->ports; + for (auto it = ports.begin(); it != ports.end(); ) { + RTLIL::Wire* w = m->wire(*it); + log_assert(w); + if (w == carry_in || w == carry_out) { + it = ports.erase(it); + continue; + } + if (w->port_id > carry_in->port_id) + --w->port_id; + if (w->port_id > carry_out->port_id) + --w->port_id; + log_assert(w->port_input || w->port_output); + log_assert(ports[w->port_id-1] == w->name); + ++it; + } + ports.push_back(carry_in->name); + carry_in->port_id = ports.size(); + ports.push_back(carry_out->name); + carry_out->port_id = ports.size(); + } } for (auto mod : design->selected_modules()) @@ -1110,7 +1147,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); + box_file, lut_file, wire_delay, box_lookup, scc_break_inputs); continue; } @@ -1256,7 +1293,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); + box_file, lut_file, wire_delay, box_lookup, scc_break_inputs); assign_map.set(mod); } } diff --git a/passes/techmap/dff2dffe.cc b/passes/techmap/dff2dffe.cc index e766f2cf6..0242256e5 100644 --- a/passes/techmap/dff2dffe.cc +++ b/passes/techmap/dff2dffe.cc @@ -265,7 +265,7 @@ struct Dff2dffePass : public Pass { log("\n"); log(" -unmap\n"); log(" operate in the opposite direction: replace $dffe cells with combinations\n"); - log(" of $dff and $mux cells. the options below are ignore in unmap mode.\n"); + log(" of $dff and $mux cells. the options below are ignored in unmap mode.\n"); log("\n"); log(" -unmap-mince N\n"); log(" Same as -unmap but only unmap $dffe where the clock enable port\n"); diff --git a/passes/techmap/techmap.cc b/passes/techmap/techmap.cc index b271c8781..c4496f76f 100644 --- a/passes/techmap/techmap.cc +++ b/passes/techmap/techmap.cc @@ -943,7 +943,8 @@ struct TechmapPass : public Pass { log(" instead of inlining them.\n"); log("\n"); log(" -max_iter <number>\n"); - log(" only run the specified number of iterations.\n"); + log(" only run the specified number of iterations on each module.\n"); + log(" default: unlimited\n"); log("\n"); log(" -recursive\n"); log(" instead of the iterative breadth-first algorithm use a recursive\n"); @@ -1157,15 +1158,16 @@ struct TechmapPass : public Pass { RTLIL::Module *module = *worker.module_queue.begin(); worker.module_queue.erase(module); + int module_max_iter = max_iter; bool did_something = true; std::set<RTLIL::Cell*> handled_cells; while (did_something) { did_something = false; - if (worker.techmap_module(design, module, map, handled_cells, celltypeMap, false)) - did_something = true; + if (worker.techmap_module(design, module, map, handled_cells, celltypeMap, false)) + did_something = true; if (did_something) module->check(); - if (max_iter > 0 && --max_iter == 0) + if (module_max_iter > 0 && --module_max_iter == 0) break; } } diff --git a/techlibs/common/synth.cc b/techlibs/common/synth.cc index 555de9fba..a176357a7 100644 --- a/techlibs/common/synth.cc +++ b/techlibs/common/synth.cc @@ -175,7 +175,7 @@ struct SynthPass : public ScriptPass log_cmd_error("This command only operates on fully selected designs!\n"); if (abc == "abc9" && !lut) - log_cmd_error("ABC9 flow only supported for FPGA synthesis (using '-lut' option)"); + log_cmd_error("ABC9 flow only supported for FPGA synthesis (using '-lut' option)\n"); log_header(design, "Executing SYNTH pass.\n"); log_push(); diff --git a/techlibs/ecp5/cells_sim.v b/techlibs/ecp5/cells_sim.v index 3d343b315..2fcb0369e 100644 --- a/techlibs/ecp5/cells_sim.v +++ b/techlibs/ecp5/cells_sim.v @@ -15,10 +15,13 @@ module L6MUX21 (input D0, D1, SD, output Z); endmodule // --------------------------------------- -(* abc_box_id=1, abc_carry="CIN,COUT", lib_whitebox *) -module CCU2C(input CIN, A0, B0, C0, D0, A1, B1, C1, D1, - output S0, S1, COUT); - +(* abc_box_id=1, lib_whitebox *) +module CCU2C( + (* abc_carry *) input CIN, + input A0, B0, C0, D0, A1, B1, C1, D1, + output S0, S1, + (* abc_carry *) output COUT +); parameter [15:0] INIT0 = 16'h0000; parameter [15:0] INIT1 = 16'h0000; parameter INJECT1_0 = "YES"; @@ -104,12 +107,13 @@ module PFUMX (input ALUT, BLUT, C0, output Z); endmodule // --------------------------------------- -//(* abc_box_id=2, abc_scc_break="DI,WAD,WRE" *) +//(* abc_box_id=2 *) module TRELLIS_DPR16X4 ( - input [3:0] DI, - input [3:0] WAD, - input WRE, WCK, - input [3:0] RAD, + (* 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, output [3:0] DO ); parameter WCKMUX = "WCK"; diff --git a/techlibs/efinix/Makefile.inc b/techlibs/efinix/Makefile.inc new file mode 100644 index 000000000..5013f7fc1 --- /dev/null +++ b/techlibs/efinix/Makefile.inc @@ -0,0 +1,10 @@ + +OBJS += techlibs/efinix/synth_efinix.o +OBJS += techlibs/efinix/efinix_gbuf.o +OBJS += techlibs/efinix/efinix_fixcarry.o + +$(eval $(call add_share_file,share/efinix,techlibs/efinix/cells_map.v)) +$(eval $(call add_share_file,share/efinix,techlibs/efinix/arith_map.v)) +$(eval $(call add_share_file,share/efinix,techlibs/efinix/cells_sim.v)) +$(eval $(call add_share_file,share/efinix,techlibs/efinix/brams_map.v)) +$(eval $(call add_share_file,share/efinix,techlibs/efinix/bram.txt)) diff --git a/techlibs/efinix/arith_map.v b/techlibs/efinix/arith_map.v new file mode 100644 index 000000000..178f57bc5 --- /dev/null +++ b/techlibs/efinix/arith_map.v @@ -0,0 +1,79 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com> + * 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. + * + */ + +(* techmap_celltype = "$alu" *) +module _80_efinix_alu (A, B, CI, BI, X, Y, CO); + 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] X, Y; + + input CI, BI; + output [Y_WIDTH-1:0] CO; + + wire CIx; + wire [Y_WIDTH-1:0] COx; + + wire _TECHMAP_FAIL_ = Y_WIDTH <= 2; + + wire [Y_WIDTH-1:0] A_buf, B_buf; + \$pos #(.A_SIGNED(A_SIGNED), .A_WIDTH(A_WIDTH), .Y_WIDTH(Y_WIDTH)) A_conv (.A(A), .Y(A_buf)); + \$pos #(.A_SIGNED(B_SIGNED), .A_WIDTH(B_WIDTH), .Y_WIDTH(Y_WIDTH)) B_conv (.A(B), .Y(B_buf)); + + wire [Y_WIDTH-1:0] AA = A_buf; + wire [Y_WIDTH-1:0] BB = BI ? ~B_buf : B_buf; + wire [Y_WIDTH-1:0] C = { COx, CIx }; + + EFX_ADD #(.I0_POLARITY(1'b1),.I1_POLARITY(1'b1)) + adder_cin ( + .I0(CI), + .I1(1'b1), + .CI(1'b0), + .CO(CIx) + ); + + genvar i; + generate for (i = 0; i < Y_WIDTH; i = i + 1) begin: slice + EFX_ADD #(.I0_POLARITY(1'b1),.I1_POLARITY(1'b1)) + adder_i ( + .I0(AA[i]), + .I1(BB[i]), + .CI(C[i]), + .O(Y[i]), + .CO(COx[i]) + ); + EFX_ADD #(.I0_POLARITY(1'b1),.I1_POLARITY(1'b1)) + adder_cout ( + .I0(1'b0), + .I1(1'b0), + .CI(COx[i]), + .O(CO[i]) + ); + end: slice + endgenerate + + /* End implementation */ + assign X = AA ^ BB; +endmodule
\ No newline at end of file diff --git a/techlibs/efinix/bram.txt b/techlibs/efinix/bram.txt new file mode 100644 index 000000000..0b3fd9308 --- /dev/null +++ b/techlibs/efinix/bram.txt @@ -0,0 +1,32 @@ +bram $__EFINIX_5K + init 1 + + abits 8 @a8d16 + dbits 16 @a8d16 + abits 9 @a9d8 + dbits 8 @a9d8 + abits 10 @a10d4 + dbits 4 @a10d4 + abits 11 @a11d2 + dbits 2 @a11d2 + abits 12 @a12d1 + dbits 1 @a12d1 + abits 8 @a8d20 + dbits 20 @a8d20 + abits 9 @a9d10 + dbits 10 @a9d10 + + groups 2 + ports 1 1 + wrmode 1 0 + enable 1 1 + transp 0 2 + clocks 2 3 + clkpol 2 3 +endbram + +match $__EFINIX_5K + min bits 256 + min efficiency 5 + shuffle_enable B +endmatch diff --git a/techlibs/efinix/brams_map.v b/techlibs/efinix/brams_map.v new file mode 100644 index 000000000..6786ae769 --- /dev/null +++ b/techlibs/efinix/brams_map.v @@ -0,0 +1,65 @@ +module \$__EFINIX_5K (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN); + parameter CFG_ABITS = 8; + parameter CFG_DBITS = 20; + parameter CFG_ENABLE_A = 1; + + parameter CLKPOL2 = 1; + parameter CLKPOL3 = 1; + parameter [5119:0] INIT = 5119'bx; + parameter TRANSP2 = 0; + + input CLK2; + input CLK3; + + input [CFG_ABITS-1:0] A1ADDR; + input [CFG_DBITS-1:0] A1DATA; + input [CFG_ENABLE_A-1:0] A1EN; + + input [CFG_ABITS-1:0] B1ADDR; + output [CFG_DBITS-1:0] B1DATA; + input B1EN; + + localparam WRITEMODE_A = TRANSP2 ? "WRITE_FIRST" : "READ_FIRST"; + + EFX_RAM_5K #( + .READ_WIDTH(CFG_DBITS), + .WRITE_WIDTH(CFG_DBITS), + .OUTPUT_REG(1'b0), + .RCLK_POLARITY(1'b1), + .RE_POLARITY(1'b1), + .WCLK_POLARITY(1'b1), + .WE_POLARITY(1'b1), + .WCLKE_POLARITY(1'b1), + .WRITE_MODE(WRITEMODE_A), + .INIT_0(INIT[ 0*256 +: 256]), + .INIT_1(INIT[ 1*256 +: 256]), + .INIT_2(INIT[ 2*256 +: 256]), + .INIT_3(INIT[ 3*256 +: 256]), + .INIT_4(INIT[ 4*256 +: 256]), + .INIT_5(INIT[ 5*256 +: 256]), + .INIT_6(INIT[ 6*256 +: 256]), + .INIT_7(INIT[ 7*256 +: 256]), + .INIT_8(INIT[ 8*256 +: 256]), + .INIT_9(INIT[ 9*256 +: 256]), + .INIT_A(INIT[10*256 +: 256]), + .INIT_B(INIT[11*256 +: 256]), + .INIT_C(INIT[12*256 +: 256]), + .INIT_D(INIT[13*256 +: 256]), + .INIT_E(INIT[14*256 +: 256]), + .INIT_F(INIT[15*256 +: 256]), + .INIT_10(INIT[16*256 +: 256]), + .INIT_11(INIT[17*256 +: 256]), + .INIT_12(INIT[18*256 +: 256]), + .INIT_13(INIT[19*256 +: 256]) + ) _TECHMAP_REPLACE_ ( + .WDATA(A1DATA), + .WADDR(A1ADDR), + .WE(A1EN), + .WCLK(CLK2), + .WCLKE(1'b1), + .RDATA(B1DATA), + .RADDR(B1ADDR), + .RE(B1EN), + .RCLK(CLK3) + ); +endmodule diff --git a/techlibs/efinix/cells_map.v b/techlibs/efinix/cells_map.v new file mode 100644 index 000000000..0aeab1902 --- /dev/null +++ b/techlibs/efinix/cells_map.v @@ -0,0 +1,45 @@ +module \$_DFF_N_ (input D, C, output Q); EFX_FF #(.CLK_POLARITY(1'b0), .CE_POLARITY(1'b1), .SR_POLARITY(1'b1), .D_POLARITY(1'b1), .SR_SYNC(1'b1), .SR_VALUE(1'b0), .SR_SYNC_PRIORITY(1'b1)) _TECHMAP_REPLACE_ (.D(D), .CE(1'b1), .CLK(C), .SR(1'b0), .Q(Q)); endmodule +module \$_DFF_P_ (input D, C, output Q); EFX_FF #(.CLK_POLARITY(1'b1), .CE_POLARITY(1'b1), .SR_POLARITY(1'b1), .D_POLARITY(1'b1), .SR_SYNC(1'b1), .SR_VALUE(1'b0), .SR_SYNC_PRIORITY(1'b1)) _TECHMAP_REPLACE_ (.D(D), .CE(1'b1), .CLK(C), .SR(1'b0), .Q(Q)); endmodule + +module \$_DFFE_NN_ (input D, C, E, output Q); EFX_FF #(.CLK_POLARITY(1'b0), .CE_POLARITY(1'b0), .SR_POLARITY(1'b1), .D_POLARITY(1'b1), .SR_SYNC(1'b1), .SR_VALUE(1'b0), .SR_SYNC_PRIORITY(1'b1)) _TECHMAP_REPLACE_ (.D(D), .CE(E), .CLK(C), .SR(1'b0), .Q(Q)); endmodule +module \$_DFFE_NP_ (input D, C, E, output Q); EFX_FF #(.CLK_POLARITY(1'b0), .CE_POLARITY(1'b1), .SR_POLARITY(1'b1), .D_POLARITY(1'b1), .SR_SYNC(1'b1), .SR_VALUE(1'b0), .SR_SYNC_PRIORITY(1'b1)) _TECHMAP_REPLACE_ (.D(D), .CE(E), .CLK(C), .SR(1'b0), .Q(Q)); endmodule + +module \$_DFFE_PN_ (input D, C, E, output Q); EFX_FF #(.CLK_POLARITY(1'b1), .CE_POLARITY(1'b0), .SR_POLARITY(1'b1), .D_POLARITY(1'b1), .SR_SYNC(1'b1), .SR_VALUE(1'b0), .SR_SYNC_PRIORITY(1'b1)) _TECHMAP_REPLACE_ (.D(D), .CE(E), .CLK(C), .SR(1'b0), .Q(Q)); endmodule +module \$_DFFE_PP_ (input D, C, E, output Q); EFX_FF #(.CLK_POLARITY(1'b1), .CE_POLARITY(1'b1), .SR_POLARITY(1'b1), .D_POLARITY(1'b1), .SR_SYNC(1'b1), .SR_VALUE(1'b0), .SR_SYNC_PRIORITY(1'b1)) _TECHMAP_REPLACE_ (.D(D), .CE(E), .CLK(C), .SR(1'b0), .Q(Q)); endmodule + +module \$_DFF_NN0_ (input D, C, R, output Q); EFX_FF #(.CLK_POLARITY(1'b0), .CE_POLARITY(1'b1), .SR_POLARITY(1'b0), .D_POLARITY(1'b1), .SR_SYNC(1'b0), .SR_VALUE(1'b0), .SR_SYNC_PRIORITY(1'b1)) _TECHMAP_REPLACE_ (.D(D), .CE(1'b1), .CLK(C), .SR(R), .Q(Q)); endmodule +module \$_DFF_NN1_ (input D, C, R, output Q); EFX_FF #(.CLK_POLARITY(1'b0), .CE_POLARITY(1'b1), .SR_POLARITY(1'b0), .D_POLARITY(1'b1), .SR_SYNC(1'b0), .SR_VALUE(1'b1), .SR_SYNC_PRIORITY(1'b1)) _TECHMAP_REPLACE_ (.D(D), .CE(1'b1), .CLK(C), .SR(R), .Q(Q)); endmodule +module \$_DFF_PN0_ (input D, C, R, output Q); EFX_FF #(.CLK_POLARITY(1'b1), .CE_POLARITY(1'b1), .SR_POLARITY(1'b0), .D_POLARITY(1'b1), .SR_SYNC(1'b0), .SR_VALUE(1'b0), .SR_SYNC_PRIORITY(1'b1)) _TECHMAP_REPLACE_ (.D(D), .CE(1'b1), .CLK(C), .SR(R), .Q(Q)); endmodule +module \$_DFF_PN1_ (input D, C, R, output Q); EFX_FF #(.CLK_POLARITY(1'b1), .CE_POLARITY(1'b1), .SR_POLARITY(1'b0), .D_POLARITY(1'b1), .SR_SYNC(1'b0), .SR_VALUE(1'b1), .SR_SYNC_PRIORITY(1'b1)) _TECHMAP_REPLACE_ (.D(D), .CE(1'b1), .CLK(C), .SR(R), .Q(Q)); endmodule + +module \$_DFF_NP0_ (input D, C, R, output Q); EFX_FF #(.CLK_POLARITY(1'b0), .CE_POLARITY(1'b1), .SR_POLARITY(1'b1), .D_POLARITY(1'b1), .SR_SYNC(1'b0), .SR_VALUE(1'b0), .SR_SYNC_PRIORITY(1'b1)) _TECHMAP_REPLACE_ (.D(D), .CE(1'b1), .CLK(C), .SR(R), .Q(Q)); endmodule +module \$_DFF_NP1_ (input D, C, R, output Q); EFX_FF #(.CLK_POLARITY(1'b0), .CE_POLARITY(1'b1), .SR_POLARITY(1'b1), .D_POLARITY(1'b1), .SR_SYNC(1'b0), .SR_VALUE(1'b1), .SR_SYNC_PRIORITY(1'b1)) _TECHMAP_REPLACE_ (.D(D), .CE(1'b1), .CLK(C), .SR(R), .Q(Q)); endmodule +module \$_DFF_PP0_ (input D, C, R, output Q); EFX_FF #(.CLK_POLARITY(1'b1), .CE_POLARITY(1'b1), .SR_POLARITY(1'b1), .D_POLARITY(1'b1), .SR_SYNC(1'b0), .SR_VALUE(1'b0), .SR_SYNC_PRIORITY(1'b1)) _TECHMAP_REPLACE_ (.D(D), .CE(1'b1), .CLK(C), .SR(R), .Q(Q)); endmodule +module \$_DFF_PP1_ (input D, C, R, output Q); EFX_FF #(.CLK_POLARITY(1'b1), .CE_POLARITY(1'b1), .SR_POLARITY(1'b1), .D_POLARITY(1'b1), .SR_SYNC(1'b0), .SR_VALUE(1'b1), .SR_SYNC_PRIORITY(1'b1)) _TECHMAP_REPLACE_ (.D(D), .CE(1'b1), .CLK(C), .SR(R), .Q(Q)); endmodule + +`ifndef NO_LUT +module \$lut (A, Y); + parameter WIDTH = 0; + parameter LUT = 0; + + input [WIDTH-1:0] A; + output Y; + + generate + if (WIDTH == 1) begin + EFX_LUT4 #(.LUTMASK(LUT)) _TECHMAP_REPLACE_ (.O(Y), .I0(A[0]), .I1(1'b0), .I2(1'b0), .I3(1'b0)); + end else + if (WIDTH == 2) begin + EFX_LUT4 #(.LUTMASK(LUT)) _TECHMAP_REPLACE_ (.O(Y), .I0(A[0]), .I1(A[1]), .I2(1'b0), .I3(1'b0)); + end else + if (WIDTH == 3) begin + EFX_LUT4 #(.LUTMASK(LUT)) _TECHMAP_REPLACE_ (.O(Y), .I0(A[0]), .I1(A[1]), .I2(A[2]), .I3(1'b0)); + end else + if (WIDTH == 4) begin + EFX_LUT4 #(.LUTMASK(LUT)) _TECHMAP_REPLACE_ (.O(Y), .I0(A[0]), .I1(A[1]), .I2(A[2]), .I3(A[3])); + end else begin + wire _TECHMAP_FAIL_ = 1; + end + endgenerate +endmodule +`endif diff --git a/techlibs/efinix/cells_sim.v b/techlibs/efinix/cells_sim.v new file mode 100644 index 000000000..8c8f6afaa --- /dev/null +++ b/techlibs/efinix/cells_sim.v @@ -0,0 +1,107 @@ +module EFX_LUT4( + output O, + input I0, + input I1, + input I2, + input I3 +); + parameter LUTMASK = 16'h0000; +endmodule + +module EFX_ADD( + output O, + output CO, + input I0, + input I1, + input CI +); + parameter I0_POLARITY = 1; + parameter I1_POLARITY = 1; +endmodule + +module EFX_FF( + output Q, + input D, + input CE, + input CLK, + input SR +); + parameter CLK_POLARITY = 1; + parameter CE_POLARITY = 1; + parameter SR_POLARITY = 1; + parameter SR_SYNC = 0; + parameter SR_VALUE = 0; + parameter SR_SYNC_PRIORITY = 0; + parameter D_POLARITY = 1; +endmodule + +module EFX_GBUFCE( + input CE, + input I, + output O +); + parameter CE_POLARITY = 1'b1; +endmodule + +module EFX_RAM_5K( + input [WRITE_WIDTH-1:0] WDATA, + input [WRITE_ADDR_WIDTH-1:0] WADDR, + input WE, + input WCLK, + input WCLKE, + output [READ_WIDTH-1:0] RDATA, + input [READ_ADDR_WIDTH-1:0] RADDR, + input RE, + input RCLK +); + parameter READ_WIDTH = 20; + parameter WRITE_WIDTH = 20; + parameter OUTPUT_REG = 1'b0; + parameter RCLK_POLARITY = 1'b1; + parameter RE_POLARITY = 1'b1; + parameter WCLK_POLARITY = 1'b1; + parameter WE_POLARITY = 1'b1; + parameter WCLKE_POLARITY = 1'b1; + parameter WRITE_MODE = "READ_FIRST"; + parameter INIT_0 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_1 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_2 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_3 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_4 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_5 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_6 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_7 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_8 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_9 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_A = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_B = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_C = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_D = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_E = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_F = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_10 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_11 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_12 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_13 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + + localparam READ_ADDR_WIDTH = + (READ_WIDTH == 16) ? 8 : // 256x16 + (READ_WIDTH == 8) ? 9 : // 512x8 + (READ_WIDTH == 4) ? 10 : // 1024x4 + (READ_WIDTH == 2) ? 11 : // 2048x2 + (READ_WIDTH == 1) ? 12 : // 4096x1 + (READ_WIDTH == 20) ? 8 : // 256x20 + (READ_WIDTH == 10) ? 9 : // 512x10 + (READ_WIDTH == 5) ? 10 : -1; // 1024x5 + + localparam WRITE_ADDR_WIDTH = + (WRITE_WIDTH == 16) ? 8 : // 256x16 + (WRITE_WIDTH == 8) ? 9 : // 512x8 + (WRITE_WIDTH == 4) ? 10 : // 1024x4 + (WRITE_WIDTH == 2) ? 11 : // 2048x2 + (WRITE_WIDTH == 1) ? 12 : // 4096x1 + (WRITE_WIDTH == 20) ? 8 : // 256x20 + (WRITE_WIDTH == 10) ? 9 : // 512x10 + (WRITE_WIDTH == 5) ? 10 : -1; // 1024x5 + +endmodule
\ No newline at end of file diff --git a/techlibs/efinix/efinix_fixcarry.cc b/techlibs/efinix/efinix_fixcarry.cc new file mode 100644 index 000000000..b7cd995b8 --- /dev/null +++ b/techlibs/efinix/efinix_fixcarry.cc @@ -0,0 +1,122 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2019 Miodrag Milanovic <miodrag@symbioticeda.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +static SigBit get_bit_or_zero(const SigSpec &sig) +{ + if (GetSize(sig) == 0) + return State::S0; + return sig[0]; +} + +static void fix_carry_chain(Module *module) +{ + SigMap sigmap(module); + + pool<SigBit> ci_bits; + dict<SigBit, SigBit> mapping_bits; + + for (auto cell : module->cells()) + { + if (cell->type == "\\EFX_ADD") { + SigBit bit_i0 = get_bit_or_zero(cell->getPort("\\I0")); + SigBit bit_i1 = get_bit_or_zero(cell->getPort("\\I1")); + if (bit_i0 == State::S0 && bit_i1== State::S0) { + SigBit bit_ci = get_bit_or_zero(cell->getPort("\\CI")); + SigBit bit_o = sigmap(cell->getPort("\\O")); + ci_bits.insert(bit_ci); + mapping_bits[bit_ci] = bit_o; + } + } + } + + vector<Cell*> adders_to_fix_cells; + for (auto cell : module->cells()) + { + if (cell->type == "\\EFX_ADD") { + SigBit bit_ci = get_bit_or_zero(cell->getPort("\\CI")); + SigBit bit_i0 = get_bit_or_zero(cell->getPort("\\I0")); + SigBit bit_i1 = get_bit_or_zero(cell->getPort("\\I1")); + SigBit canonical_bit = sigmap(bit_ci); + if (!ci_bits.count(canonical_bit)) + continue; + if (bit_i0 == State::S0 && bit_i1== State::S0) + continue; + + adders_to_fix_cells.push_back(cell); + log("Found %s cell named %s with invalid CI signal.\n", log_id(cell->type), log_id(cell)); + } + } + + for (auto cell : adders_to_fix_cells) + { + SigBit bit_ci = get_bit_or_zero(cell->getPort("\\CI")); + SigBit canonical_bit = sigmap(bit_ci); + auto bit = mapping_bits.at(canonical_bit); + log("Fixing %s cell named %s breaking carry chain.\n", log_id(cell->type), log_id(cell)); + Cell *c = module->addCell(NEW_ID, "\\EFX_ADD"); + SigBit new_bit = module->addWire(NEW_ID); + c->setParam("\\I0_POLARITY", State::S1); + c->setParam("\\I1_POLARITY", State::S1); + c->setPort("\\I0", bit); + c->setPort("\\I1", State::S1); + c->setPort("\\CI", State::S0); + c->setPort("\\CO", new_bit); + + cell->setPort("\\CI", new_bit); + } +} + +struct EfinixCarryFixPass : public Pass { + EfinixCarryFixPass() : Pass("efinix_fixcarry", "Efinix: fix carry chain") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" efinix_fixcarry [options] [selection]\n"); + log("\n"); + log("Add Efinix adders to fix carry chain if needed.\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing efinix_fixcarry pass (fix invalid carry chain).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + break; + } + extra_args(args, argidx, design); + + Module *module = design->top_module(); + + if (module == nullptr) + log_cmd_error("No top module found.\n"); + + fix_carry_chain(module); + } +} EfinixCarryFixPass; + +PRIVATE_NAMESPACE_END diff --git a/techlibs/efinix/efinix_gbuf.cc b/techlibs/efinix/efinix_gbuf.cc new file mode 100644 index 000000000..e75fb3f4d --- /dev/null +++ b/techlibs/efinix/efinix_gbuf.cc @@ -0,0 +1,119 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2019 Miodrag Milanovic <miodrag@symbioticeda.com> + * 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 + +static void handle_gbufs(Module *module) +{ + SigMap sigmap(module); + + pool<SigBit> clk_bits; + dict<SigBit, SigBit> rewrite_bits; + vector<pair<Cell*, SigBit>> pad_bits; + + for (auto cell : module->cells()) + { + if (cell->type == "\\EFX_FF") { + for (auto bit : sigmap(cell->getPort("\\CLK"))) + clk_bits.insert(bit); + } + if (cell->type == "\\EFX_RAM_5K") { + for (auto bit : sigmap(cell->getPort("\\RCLK"))) + clk_bits.insert(bit); + for (auto bit : sigmap(cell->getPort("\\WCLK"))) + clk_bits.insert(bit); + } + } + + for (auto wire : vector<Wire*>(module->wires())) + { + if (!wire->port_input) + continue; + + for (int index = 0; index < GetSize(wire); index++) + { + SigBit bit(wire, index); + SigBit canonical_bit = sigmap(bit); + + if (!clk_bits.count(canonical_bit)) + continue; + + Cell *c = module->addCell(NEW_ID, "\\EFX_GBUFCE"); + SigBit new_bit = module->addWire(NEW_ID); + c->setParam("\\CE_POLARITY", State::S1); + c->setPort("\\O", new_bit); + c->setPort("\\CE", State::S1); + pad_bits.push_back(make_pair(c, bit)); + rewrite_bits[canonical_bit] = new_bit; + + log("Added %s cell %s for port bit %s.\n", log_id(c->type), log_id(c), log_signal(bit)); + } + } + + auto rewrite_function = [&](SigSpec &s) { + for (auto &bit : s) { + SigBit canonical_bit = sigmap(bit); + if (rewrite_bits.count(canonical_bit)) + bit = rewrite_bits.at(canonical_bit); + } + }; + + module->rewrite_sigspecs(rewrite_function); + + for (auto &it : pad_bits) + it.first->setPort("\\I", it.second); +} + +struct EfinixGbufPass : public Pass { + EfinixGbufPass() : Pass("efinix_gbuf", "Efinix: insert global clock buffers") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" efinix_gbuf [options] [selection]\n"); + log("\n"); + log("Add Efinix global clock buffers to top module as needed.\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing efinix_gbuf pass (insert global clock buffers).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + break; + } + extra_args(args, argidx, design); + + Module *module = design->top_module(); + + if (module == nullptr) + log_cmd_error("No top module found.\n"); + + handle_gbufs(module); + } +} EfinixGbufPass; + +PRIVATE_NAMESPACE_END diff --git a/techlibs/efinix/synth_efinix.cc b/techlibs/efinix/synth_efinix.cc new file mode 100644 index 000000000..26a8d4eda --- /dev/null +++ b/techlibs/efinix/synth_efinix.cc @@ -0,0 +1,219 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2019 Miodrag Milanovic <miodrag@symbioticeda.com> + * Copyright (C) 2019 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/register.h" +#include "kernel/celltypes.h" +#include "kernel/rtlil.h" +#include "kernel/log.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct SynthEfinixPass : public ScriptPass +{ + SynthEfinixPass() : ScriptPass("synth_efinix", "synthesis for Efinix FPGAs") { } + + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" synth_efinix [options]\n"); + log("\n"); + log("This command runs synthesis for Efinix FPGAs.\n"); + log("\n"); + log(" -top <module>\n"); + log(" use the specified module as top module\n"); + log("\n"); + log(" -edif <file>\n"); + log(" write the design to the specified EDIF file. writing of an output file\n"); + log(" is omitted if this parameter is not specified.\n"); + log("\n"); + log(" -json <file>\n"); + log(" write the design to the specified JSON file. writing of an output file\n"); + log(" is omitted if this parameter is not specified.\n"); + log("\n"); + log(" -run <from_label>:<to_label>\n"); + log(" only run the commands between the labels (see below). an empty\n"); + log(" from label is synonymous to 'begin', and empty to label is\n"); + log(" synonymous to the end of the command list.\n"); + log("\n"); + log(" -noflatten\n"); + log(" do not flatten design before synthesis\n"); + log("\n"); + log(" -retime\n"); + log(" run 'abc' with -dff option\n"); + log("\n"); + log("\n"); + log("The following commands are executed by this synthesis command:\n"); + help_script(); + log("\n"); + } + + string top_opt, edif_file, json_file; + bool flatten, retime; + + void clear_flags() YS_OVERRIDE + { + top_opt = "-auto-top"; + edif_file = ""; + json_file = ""; + flatten = true; + retime = false; + } + + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + string run_from, run_to; + clear_flags(); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-top" && argidx+1 < args.size()) { + top_opt = "-top " + args[++argidx]; + continue; + } + if (args[argidx] == "-edif" && argidx+1 < args.size()) { + edif_file = args[++argidx]; + continue; + } + if (args[argidx] == "-json" && argidx+1 < args.size()) { + json_file = args[++argidx]; + continue; + } + if (args[argidx] == "-run" && argidx+1 < args.size()) { + size_t pos = args[argidx+1].find(':'); + if (pos == std::string::npos) + break; + run_from = args[++argidx].substr(0, pos); + run_to = args[argidx].substr(pos+1); + continue; + } + if (args[argidx] == "-noflatten") { + flatten = false; + continue; + } + if (args[argidx] == "-retime") { + retime = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + if (!design->full_selection()) + log_cmd_error("This command only operates on fully selected designs!\n"); + + log_header(design, "Executing SYNTH_EFINIX pass.\n"); + log_push(); + + run_script(design, run_from, run_to); + + log_pop(); + } + + void script() YS_OVERRIDE + { + if (check_label("begin")) + { + run("read_verilog -lib +/efinix/cells_sim.v"); + run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str())); + } + + if (flatten && check_label("flatten", "(unless -noflatten)")) + { + run("proc"); + run("flatten"); + run("tribuf -logic"); + run("deminout"); + } + + if (check_label("coarse")) + { + run("synth -run coarse"); + } + + if (check_label("map_bram", "(skip if -nobram)")) + { + run("memory_bram -rules +/efinix/bram.txt"); + run("techmap -map +/efinix/brams_map.v"); + run("setundef -zero -params t:EFX_RAM_5K"); + } + + if (check_label("fine")) + { + run("opt -fast -mux_undef -undriven -fine"); + run("memory_map"); + run("opt -undriven -fine"); + run("techmap -map +/techmap.v -map +/efinix/arith_map.v"); + if (retime || help_mode) + run("abc -dff", "(only if -retime)"); + } + + if (check_label("map_ffs")) + { + run("dffsr2dff"); + run("techmap -D NO_LUT -map +/efinix/cells_map.v"); + run("dffinit -strinit SET RESET -ff AL_MAP_SEQ q REGSET -noreinit"); + run("opt_expr -mux_undef"); + run("simplemap"); + } + + if (check_label("map_luts")) + { + run("abc -lut 4"); + run("clean"); + } + + if (check_label("map_cells")) + { + run("techmap -map +/efinix/cells_map.v"); + run("clean"); + } + + if (check_label("map_gbuf")) + { + run("efinix_gbuf"); + run("efinix_fixcarry"); + run("clean"); + } + + if (check_label("check")) + { + run("hierarchy -check"); + run("stat"); + run("check -noinit"); + } + + if (check_label("edif")) + { + if (!edif_file.empty() || help_mode) + run(stringf("write_edif %s", help_mode ? "<file-name>" : edif_file.c_str())); + } + + if (check_label("json")) + { + if (!json_file.empty() || help_mode) + run(stringf("write_json %s", help_mode ? "<file-name>" : json_file.c_str())); + } + } +} SynthEfinixPass; + +PRIVATE_NAMESPACE_END diff --git a/techlibs/ice40/cells_sim.v b/techlibs/ice40/cells_sim.v index 2205be27d..ab04808f4 100644 --- a/techlibs/ice40/cells_sim.v +++ b/techlibs/ice40/cells_sim.v @@ -141,8 +141,14 @@ module SB_CARRY (output CO, input I0, I1, CI); assign CO = (I0 && I1) || ((I0 || I1) && CI); endmodule -(* abc_box_id = 1, abc_carry="CI,CO", lib_whitebox *) -module \$__ICE40_FULL_ADDER (output CO, O, input A, B, CI); +(* abc_box_id = 1, lib_whitebox *) +module \$__ICE40_FULL_ADDER ( + (* abc_carry *) output CO, + output O, + input A, + input B, + (* abc_carry *) input CI +); SB_CARRY carry ( .I0(A), .I1(B), diff --git a/techlibs/xilinx/Makefile.inc b/techlibs/xilinx/Makefile.inc index 2c6e7432e..2b1af289c 100644 --- a/techlibs/xilinx/Makefile.inc +++ b/techlibs/xilinx/Makefile.inc @@ -32,8 +32,8 @@ $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc6s_brams_bb.v)) $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc7_brams.txt)) $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc7_brams_map.v)) $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc7_brams_bb.v)) -$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/drams.txt)) -$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/drams_map.v)) +$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/lutrams.txt)) +$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/lutrams_map.v)) $(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)) diff --git a/techlibs/xilinx/cells_sim.v b/techlibs/xilinx/cells_sim.v index 05e46b4e7..bec9ea1a0 100644 --- a/techlibs/xilinx/cells_sim.v +++ b/techlibs/xilinx/cells_sim.v @@ -181,8 +181,14 @@ module XORCY(output O, input CI, LI); assign O = CI ^ LI; endmodule -(* abc_box_id = 4, abc_carry="CI,CO", lib_whitebox *) -module CARRY4(output [3:0] CO, O, input CI, CYINIT, input [3:0] DI, S); +(* abc_box_id = 4, lib_whitebox *) +module CARRY4( + (* abc_carry *) output [3:0] CO, + output [3:0] O, + (* abc_carry *) input CI, + input CYINIT, + input [3:0] DI, S +); assign O = S ^ {CO[2:0], CI | CYINIT}; assign CO[0] = S[0] ? CI | CYINIT : DI[0]; assign CO[1] = S[1] ? CO[0] : DI[1]; @@ -289,10 +295,12 @@ module FDPE_1 (output reg Q, input C, CE, D, PRE); always @(negedge C, posedge PRE) if (PRE) Q <= 1'b1; else if (CE) Q <= D; endmodule -(* abc_box_id = 5, abc_scc_break="D,WE" *) +(* abc_box_id = 5 *) module RAM32X1D ( output DPO, SPO, - input D, WCLK, WE, + (* abc_scc_break *) input D, + input WCLK, + (* abc_scc_break *) input WE, input A0, A1, A2, A3, A4, input DPRA0, DPRA1, DPRA2, DPRA3, DPRA4 ); @@ -307,10 +315,12 @@ module RAM32X1D ( always @(posedge clk) if (WE) mem[a] <= D; endmodule -(* abc_box_id = 6, abc_scc_break="D,WE" *) +(* abc_box_id = 6 *) module RAM64X1D ( output DPO, SPO, - input D, WCLK, WE, + (* abc_scc_break *) input D, + input WCLK, + (* abc_scc_break *) input WE, input A0, A1, A2, A3, A4, A5, input DPRA0, DPRA1, DPRA2, DPRA3, DPRA4, DPRA5 ); @@ -325,10 +335,12 @@ module RAM64X1D ( always @(posedge clk) if (WE) mem[a] <= D; endmodule -(* abc_box_id = 7, abc_scc_break="D,WE" *) +(* abc_box_id = 7 *) module RAM128X1D ( output DPO, SPO, - input D, WCLK, WE, + (* abc_scc_break *) input D, + input WCLK, + (* abc_scc_break *) input WE, input [6:0] A, DPRA ); parameter INIT = 128'h0; diff --git a/techlibs/xilinx/drams.txt b/techlibs/xilinx/lutrams.txt index 2613c206c..2613c206c 100644 --- a/techlibs/xilinx/drams.txt +++ b/techlibs/xilinx/lutrams.txt diff --git a/techlibs/xilinx/drams_map.v b/techlibs/xilinx/lutrams_map.v index 77041ca86..77041ca86 100644 --- a/techlibs/xilinx/drams_map.v +++ b/techlibs/xilinx/lutrams_map.v diff --git a/techlibs/xilinx/synth_xilinx.cc b/techlibs/xilinx/synth_xilinx.cc index d143c6823..7ba67409b 100644 --- a/techlibs/xilinx/synth_xilinx.cc +++ b/techlibs/xilinx/synth_xilinx.cc @@ -64,13 +64,13 @@ struct SynthXilinxPass : public ScriptPass log(" (this feature is experimental and incomplete)\n"); log("\n"); log(" -nobram\n"); - log(" disable inference of block rams\n"); + log(" do not use block RAM cells in output netlist\n"); log("\n"); - log(" -nodram\n"); - log(" disable inference of distributed rams\n"); + log(" -nolutram\n"); + log(" do not use distributed RAM cells in output netlist\n"); log("\n"); log(" -nosrl\n"); - log(" disable inference of shift registers\n"); + log(" do not use distributed SRL cells in output netlist\n"); log("\n"); log(" -nocarry\n"); log(" do not use XORCY/MUXCY/CARRY4 cells in output netlist\n"); @@ -104,7 +104,7 @@ struct SynthXilinxPass : public ScriptPass } std::string top_opt, edif_file, blif_file, family; - bool flatten, retime, vpr, nobram, nodram, nosrl, nocarry, nowidelut, abc9; + bool flatten, retime, vpr, nobram, nolutram, nosrl, nocarry, nowidelut, abc9; int widemux; void clear_flags() YS_OVERRIDE @@ -118,7 +118,7 @@ struct SynthXilinxPass : public ScriptPass vpr = false; nocarry = false; nobram = false; - nodram = false; + nolutram = false; nosrl = false; nocarry = false; nowidelut = false; @@ -186,8 +186,8 @@ struct SynthXilinxPass : public ScriptPass nobram = true; continue; } - if (args[argidx] == "-nodram") { - nodram = true; + if (args[argidx] == "-nolutram" || /*deprecated alias*/ args[argidx] == "-nodram") { + nolutram = true; continue; } if (args[argidx] == "-nosrl") { @@ -284,7 +284,7 @@ struct SynthXilinxPass : public ScriptPass run("opt_clean"); } - if (check_label("bram", "(skip if '-nobram')")) { + if (check_label("map_bram", "(skip if '-nobram')")) { if (help_mode) { run("memory_bram -rules +/xilinx/{family}_brams.txt"); run("techmap -map +/xilinx/{family}_brams_map.v"); @@ -301,20 +301,23 @@ struct SynthXilinxPass : public ScriptPass } } - if (check_label("dram", "(skip if '-nodram')")) { - if (!nodram || help_mode) { - run("memory_bram -rules +/xilinx/drams.txt"); - run("techmap -map +/xilinx/drams_map.v"); + if (check_label("map_lutram", "(skip if '-nolutram')")) { + if (!nolutram || help_mode) { + run("memory_bram -rules +/xilinx/lutrams.txt"); + run("techmap -map +/xilinx/lutrams_map.v"); } } - if (check_label("fine")) { + if (check_label("map_ffram")) { if (widemux > 0) run("opt -fast -mux_bool -undriven -fine"); // Necessary to omit -mux_undef otherwise muxcover // performs less efficiently else run("opt -fast -full"); run("memory_map"); + } + + if (check_label("fine")) { run("dffsr2dff"); run("dff2dffe"); if (help_mode) { diff --git a/tests/fsm/run-test.sh b/tests/fsm/run-test.sh index cf506470d..fbdcbf048 100755 --- a/tests/fsm/run-test.sh +++ b/tests/fsm/run-test.sh @@ -6,7 +6,7 @@ set -e OPTIND=1 -count=100 +count=50 seed="" # default to no seed specified while getopts "c:S:" opt do diff --git a/tests/opt/opt_rmdff.ys b/tests/opt/opt_rmdff.ys index 081f81782..83a162f44 100644 --- a/tests/opt/opt_rmdff.ys +++ b/tests/opt/opt_rmdff.ys @@ -19,8 +19,8 @@ hierarchy -top equiv equiv_simple -undef equiv_status -assert -design -load gold -stat - -design -load gate -stat +#design -load gold +#stat +# +#design -load gate +#stat diff --git a/tests/proc/.gitignore b/tests/proc/.gitignore new file mode 100644 index 000000000..397b4a762 --- /dev/null +++ b/tests/proc/.gitignore @@ -0,0 +1 @@ +*.log diff --git a/tests/proc/bug_1268.v b/tests/proc/bug_1268.v new file mode 100644 index 000000000..698ac937a --- /dev/null +++ b/tests/proc/bug_1268.v @@ -0,0 +1,23 @@ +module gold (input clock, ctrl, din, output reg dout); + always @(posedge clock) begin + if (1'b1) begin + if (1'b0) begin end else begin + dout <= 0; + end + if (ctrl) + dout <= din; + end + end +endmodule + +module gate (input clock, ctrl, din, output reg dout); + always @(posedge clock) begin + if (1'b1) begin + if (1'b0) begin end else begin + dout <= 0; + end + end + if (ctrl) + dout <= din; + end +endmodule diff --git a/tests/proc/bug_1268.ys b/tests/proc/bug_1268.ys new file mode 100644 index 000000000..b73e94449 --- /dev/null +++ b/tests/proc/bug_1268.ys @@ -0,0 +1,5 @@ +read_verilog bug_1268.v +proc +equiv_make gold gate equiv +equiv_induct +equiv_status -assert diff --git a/tests/proc/run-test.sh b/tests/proc/run-test.sh new file mode 100755 index 000000000..44ce7e674 --- /dev/null +++ b/tests/proc/run-test.sh @@ -0,0 +1,6 @@ +#!/bin/bash +set -e +for x in *.ys; do + echo "Running $x.." + ../../yosys -ql ${x%.ys}.log $x +done diff --git a/tests/simple/realexpr.v b/tests/simple/realexpr.v index 5b756e6be..74ed8faa5 100644 --- a/tests/simple/realexpr.v +++ b/tests/simple/realexpr.v @@ -1,4 +1,3 @@ - module demo_001(y1, y2, y3, y4); output [7:0] y1, y2, y3, y4; @@ -22,3 +21,13 @@ module demo_002(y0, y1, y2, y3); assign y3 = 1 ? -1 : 'd0; endmodule +module demo_003(output A, B); + parameter real p = 0; + assign A = (p==1.0); + assign B = (p!="1.000000"); +endmodule + +module demo_004(output A, B, C, D); + demo_003 #(1.0) demo_real (A, B); + demo_003 #(1) demo_int (C, D); +endmodule diff --git a/tests/simple_abc9/.gitignore b/tests/simple_abc9/.gitignore index 598951333..2355aea29 100644 --- a/tests/simple_abc9/.gitignore +++ b/tests/simple_abc9/.gitignore @@ -1,3 +1,4 @@ *.v +*.sv *.log *.out diff --git a/tests/simple_abc9/run-test.sh b/tests/simple_abc9/run-test.sh index 4935d41ad..49ae23338 100755 --- a/tests/simple_abc9/run-test.sh +++ b/tests/simple_abc9/run-test.sh @@ -18,5 +18,6 @@ if ! which iverilog > /dev/null ; then fi cp ../simple/*.v . +cp ../simple/*.sv . DOLLAR='?' -exec ${MAKE:-make} -f ../tools/autotest.mk $seed *.v EXTRA_FLAGS="-p 'hierarchy; synth -run coarse; opt -full; techmap; abc9 -lut 4 -box ../abc.box; stat; check -assert; select -assert-none t:${DOLLAR}_NOT_ t:${DOLLAR}_AND_ %%'" +exec ${MAKE:-make} -f ../tools/autotest.mk $seed *.v EXTRA_FLAGS="-n 300 -p 'hierarchy; synth -run coarse; opt -full; techmap; abc9 -lut 4 -box ../abc.box; stat; check -assert; select -assert-none t:${DOLLAR}_NOT_ t:${DOLLAR}_AND_ %%'" diff --git a/tests/techmap/recursive.v b/tests/techmap/recursive.v new file mode 100644 index 000000000..d281b21d8 --- /dev/null +++ b/tests/techmap/recursive.v @@ -0,0 +1,8 @@ +module top; +sub s0(); +foo f0(); +endmodule + +module foo; +sub s0(); +endmodule diff --git a/tests/techmap/recursive_map.v b/tests/techmap/recursive_map.v new file mode 100644 index 000000000..934256552 --- /dev/null +++ b/tests/techmap/recursive_map.v @@ -0,0 +1,4 @@ +module sub; + sub _TECHMAP_REPLACE_ (); + bar f0(); +endmodule diff --git a/tests/techmap/recursive_runtest.sh b/tests/techmap/recursive_runtest.sh new file mode 100644 index 000000000..30c79bf03 --- /dev/null +++ b/tests/techmap/recursive_runtest.sh @@ -0,0 +1,3 @@ +set -ev + +../../yosys -p 'hierarchy -top top; techmap -map recursive_map.v -max_iter 1; select -assert-count 2 t:sub; select -assert-count 2 t:bar' recursive.v diff --git a/tests/various/.gitignore b/tests/various/.gitignore index 31078b298..4b286fd61 100644 --- a/tests/various/.gitignore +++ b/tests/various/.gitignore @@ -2,3 +2,4 @@ /*.out /write_gzip.v /write_gzip.v.gz +/run-test.mk diff --git a/tests/various/mem2reg.ys b/tests/various/mem2reg.ys new file mode 100644 index 000000000..00389c700 --- /dev/null +++ b/tests/various/mem2reg.ys @@ -0,0 +1,13 @@ +read_verilog <<EOT +module top; +parameter DATADEPTH=2; +parameter DATAWIDTH=1; +(* keep, nomem2reg *) reg [DATAWIDTH-1:0] data1 [DATADEPTH-1:0]; +(* keep, mem2reg *) reg [DATAWIDTH-1:0] data2 [DATADEPTH-1:0]; +endmodule +EOT + +proc +cd top +select -assert-count 1 m:data1 a:src=<<EOT:4 %i +select -assert-count 2 w:data2[*] a:src=<<EOT:5 %i diff --git a/tests/various/muxpack.ys b/tests/various/muxpack.ys index af23fcec8..3e90419af 100644 --- a/tests/various/muxpack.ys +++ b/tests/various/muxpack.ys @@ -6,7 +6,7 @@ prep design -save gold muxpack opt -stat +#stat select -assert-count 0 t:$mux select -assert-count 1 t:$pmux design -stash gate @@ -21,7 +21,7 @@ prep design -save gold muxpack opt -stat +#stat select -assert-count 0 t:$mux select -assert-count 1 t:$pmux design -stash gate @@ -52,7 +52,7 @@ prep design -save gold muxpack opt -stat +#stat select -assert-count 0 t:$mux select -assert-count 2 t:$pmux design -stash gate @@ -67,7 +67,7 @@ prep design -save gold muxpack opt -stat +#stat select -assert-count 0 t:$mux select -assert-count 1 t:$pmux design -stash gate @@ -82,7 +82,7 @@ prep design -save gold muxpack opt -stat +#stat select -assert-count 0 t:$mux select -assert-count 1 t:$pmux design -stash gate @@ -97,7 +97,7 @@ prep design -save gold muxpack opt -stat +#stat select -assert-count 0 t:$mux select -assert-count 1 t:$pmux design -stash gate @@ -112,7 +112,7 @@ prep design -save gold muxpack opt -stat +#stat select -assert-count 0 t:$mux select -assert-count 1 t:$pmux design -stash gate @@ -127,7 +127,7 @@ prep design -save gold muxpack opt -stat +#stat select -assert-count 0 t:$mux select -assert-count 1 t:$pmux design -stash gate @@ -142,7 +142,7 @@ prep design -save gold muxpack opt -stat +#stat select -assert-count 7 t:$mux select -assert-count 0 t:$pmux design -stash gate @@ -157,7 +157,7 @@ prep design -save gold muxpack opt -stat +#stat select -assert-count 4 t:$mux select -assert-count 0 t:$pmux design -stash gate @@ -172,7 +172,7 @@ prep design -save gold muxpack opt -stat +#stat select -assert-count 3 t:$mux select -assert-count 0 t:$pmux design -stash gate @@ -204,7 +204,7 @@ prep design -save gold muxpack opt -stat +#stat select -assert-count 0 t:$mux select -assert-count 2 t:$pmux design -stash gate @@ -222,7 +222,7 @@ opt -fast -mux_undef select -assert-count 2 t:$pmux muxpack opt -stat +#stat select -assert-count 0 t:$mux select -assert-count 1 t:$pmux design -stash gate @@ -240,7 +240,7 @@ opt -fast -mux_undef select -assert-count 2 t:$pmux muxpack opt -stat +#stat select -assert-count 0 t:$mux select -assert-count 2 t:$pmux design -stash gate @@ -258,7 +258,7 @@ opt -fast -mux_undef select -assert-count 2 t:$pmux muxpack opt -stat +#stat select -assert-count 0 t:$mux select -assert-count 2 t:$pmux design -stash gate diff --git a/tests/various/pmgen_reduce.ys b/tests/various/pmgen_reduce.ys new file mode 100644 index 000000000..c214d3f25 --- /dev/null +++ b/tests/various/pmgen_reduce.ys @@ -0,0 +1,21 @@ +test_pmgen -generate reduce +hierarchy -top pmtest_test_pmgen_pm_reduce +flatten; opt_clean + +design -save gold +test_pmgen -reduce_chain +design -stash gate + +design -copy-from gold -as gold pmtest_test_pmgen_pm_reduce +design -copy-from gate -as gate pmtest_test_pmgen_pm_reduce +miter -equiv -flatten -make_assert gold gate miter +sat -verify -prove-asserts miter + +design -load gold +test_pmgen -reduce_tree +design -stash gate + +design -copy-from gold -as gold pmtest_test_pmgen_pm_reduce +design -copy-from gate -as gate pmtest_test_pmgen_pm_reduce +miter -equiv -flatten -make_assert gold gate miter +sat -verify -prove-asserts miter diff --git a/tests/various/run-test.sh b/tests/various/run-test.sh index 92b905765..ea56b70f0 100755 --- a/tests/various/run-test.sh +++ b/tests/various/run-test.sh @@ -1,12 +1,20 @@ #!/usr/bin/env bash set -e +{ +echo "all::" for x in *.ys; do - echo "Running $x.." - ../../yosys -ql ${x%.ys}.log $x + echo "all:: run-$x" + echo "run-$x:" + echo " @echo 'Running $x..'" + echo " @../../yosys -ql ${x%.ys}.log $x" done for s in *.sh; do if [ "$s" != "run-test.sh" ]; then - echo "Running $s.." - bash $s + echo "all:: run-$s" + echo "run-$s:" + echo " @echo 'Running $s..'" + echo " @bash $s" fi done +} > run-test.mk +exec ${MAKE:-make} -f run-test.mk diff --git a/tests/various/shregmap.ys b/tests/various/shregmap.ys index d644a88aa..0e5fe882b 100644 --- a/tests/various/shregmap.ys +++ b/tests/various/shregmap.ys @@ -11,7 +11,7 @@ shregmap -init opt -stat +# stat # show -width select -assert-count 1 t:$_DFF_P_ select -assert-count 2 t:$__SHREG_DFF_P_ @@ -26,11 +26,11 @@ prep miter -equiv -flatten -make_assert -make_outputs gold gate miter sat -verify -prove-asserts -show-ports -seq 5 miter -design -load gold -stat +#design -load gold +#stat -design -load gate -stat +#design -load gate +#stat ########## @@ -43,9 +43,9 @@ design -save gold simplemap t:$dff t:$dffe shregmap -tech xilinx -stat +#stat # show -width -write_verilog -noexpr -norename +# write_verilog -noexpr -norename select -assert-count 1 t:$_DFF_P_ select -assert-count 2 t:$__XILINX_SHREG_ @@ -59,8 +59,8 @@ prep miter -equiv -flatten -make_assert -make_outputs gold gate miter sat -verify -prove-asserts -show-ports -seq 5 miter -design -load gold -stat +# design -load gold +# stat -design -load gate -stat +# design -load gate +# stat |