diff options
33 files changed, 1901 insertions, 409 deletions
@@ -12,7 +12,10 @@ Yosys 0.9 .. Yosys 0.9-dev - Added "synth_xilinx -abc9" (experimental) - Added "synth_ice40 -abc9" (experimental) - Added "synth -abc9" (experimental) - - Added "script -scriptwire + - Added "script -scriptwire" + - Added "synth_xilinx -nocarry" + - Added "synth_xilinx -nowidelut" + - Added "synth_ecp5 -nowidelut" - "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) @@ -23,37 +26,135 @@ Yosys 0.9 .. Yosys 0.9-dev - 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]* + - Added "clkbufmap" pass + - Added "synth_xilinx -family xc6s" for Spartan 6 support (experimental) + - Added "synth_xilinx -ise" (experimental) + - Added "synth_xilinx -iopad" + - "synth_xilinx" now automatically inserts clock buffers (add -noclkbuf to disable) - 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" + - Improvements in pmgen: slices, choices, define, generate -Yosys 0.8 .. Yosys 0.8-dev --------------------------- +Yosys 0.8 .. Yosys 0.9 +---------------------- * Various - - Added $changed support to read_verilog + - Many bugfixes and small improvements + - Added support for SystemVerilog interfaces and modports - Added "write_edif -attrprop" - - Added "ice40_unlut" pass - Added "opt_lut" pass - - Added "synth_ice40 -relut" - - Added "synth_ice40 -noabc" - Added "gate2lut.v" techmap rule - Added "rename -src" - Added "equiv_opt" pass - - Added "shregmap -tech xilinx" + - Added "flowmap" LUT mapping pass + - Added "rename -wire" to rename cells based on the wires they drive + - Added "bugpoint" for creating minimised testcases + - Added "write_edif -gndvccy" + - "write_verilog" to escape Verilog keywords + - Fixed sign handling of real constants + - "write_verilog" to write initial statement for initial flop state + - Added pmgen pattern matcher generator + - Fixed opt_rmdff handling of $_DFFSR_???_ and $_DLATCHSR_???_ + - Added "setundef -params" to replace undefined cell parameters + - Renamed "yosys -D" to "yosys -U", added "yosys -D" to set Verilog defines + - Fixed handling of defparam when default_nettype is none + - Fixed "wreduce" flipflop handling + - Fixed FIRRTL to Verilog process instance subfield assignment + - Added "write_verilog -siminit" + - Several fixes and improvements for mem2reg memories + - Fixed handling of task output ports in clocked always blocks + - Improved handling of and-with-1 and or-with-0 in "opt_expr" - Added "read_aiger" frontend + - Added "mutate" pass + - Added "hdlname" attribute + - Added "rename -output" + - Added "read_ilang -lib" + - Improved "proc" full_case detection and handling + - Added "whitebox" and "lib_whitebox" attributes + - Added "read_verilog -nowb", "flatten -wb" and "wbflip" + - Added Python bindings and support for Python plug-ins + - Added "pmux2shiftx" + - Added log_debug framework for reduced default verbosity + - Improved "opt_expr" and "opt_clean" handling of (partially) undriven and/or unused wires + - Added "peepopt" peephole optimisation pass using pmgen + - Added approximate support for SystemVerilog "var" keyword + - Added parsing of "specify" blocks into $specrule and $specify[23] + - Added support for attributes on parameters and localparams + - Added support for parsing attributes on port connections + - Added "wreduce -keepdc" + - Added support for optimising $dffe and $_DFFE_* cells in "opt_rmdff" + - Added Verilog wand/wor wire type support + - Added support for elaboration system tasks - Added "muxcover -mux{4,8,16}=<cost>" - Added "muxcover -dmux=<cost>" - Added "muxcover -nopartial" - Added "muxpack" pass - Added "pmux2shiftx -norange" + - Added support for "~" in filename parsing + - Added "read_verilog -pwires" feature to turn parameters into wires + - Fixed sign extension of unsized constants with 'bx and 'bz MSB + - Fixed genvar to be a signed type + - Added support for attributes on case rules + - Added "upto" and "offset" to JSON frontend and backend + - Several liberty file parser improvements + - Fixed handling of more complex BRAM patterns + - Add "write_aiger -I -O -B" + + * Formal Verification + - Added $changed support to read_verilog + - Added "read_verilog -noassert -noassume -assert-assumes" + - Added btor ops for $mul, $div, $mod and $concat + - Added yosys-smtbmc support for btor witnesses + - Added "supercover" pass + - Fixed $global_clock handling vs autowire + - Added $dffsr support to "async2sync" + - Added "fmcombine" pass + - Added memory init support in "write_btor" + - Added "cutpoint" pass + - Changed "ne" to "neq" in btor2 output + - Added support for SVA "final" keyword + - Added "fmcombine -initeq -anyeq" + - Added timescale and generated-by header to yosys-smtbmc vcd output + - Improved BTOR2 handling of undriven wires + + * Verific support + - Enabled Verific flags vhdl_support_variable_slice and veri_elaborate_top_level_modules_having_interface_ports + - Improved support for asymmetric memories + - Added "verific -chparam" + - Fixed "verific -extnets" for more complex situations + - Added "read -verific" and "read -noverific" + - Added "hierarchy -chparam" + + * New back-ends + - Added initial Anlogic support + - Added initial SmartFusion2 and IGLOO2 support + + * ECP5 support + - Added "synth_ecp5 -nowidelut" + - Added BRAM inference support to "synth_ecp5" + - Added support for transforming Diamond IO and flipflop primitives + + * iCE40 support + - Added "ice40_unlut" pass + - Added "synth_ice40 -relut" + - Added "synth_ice40 -noabc" + - Added "synth_ice40 -dffe_min_ce_use" + - Added DSP inference support using pmgen + - Added support for initialising BRAM primitives from a file + - Added iCE40 Ultra RGB LED driver cells + + * Xilinx support + - Use "write_edif -pvector bra" for Xilinx EDIF files + - Fixes for VPR place and route support with "synth_xilinx" + - Added more cell simulation models + - Added "synth_xilinx -family" + - Added "stat -tech xilinx" to estimate logic cell usage - Added "synth_xilinx -nocarry" - Added "synth_xilinx -nowidelut" - - Added "synth_ecp5 -nowidelut" - "synth_xilinx" to now infer hard shift registers (-nosrl to disable) - - Fixed sign extension of unsized constants with 'bx and 'bz MSB - + - Added support for mapping RAM32X1D Yosys 0.7 .. Yosys 0.8 ---------------------- diff --git a/CodingReadme b/CodingReadme index b64e79178..8212436e5 100644 --- a/CodingReadme +++ b/CodingReadme @@ -390,6 +390,7 @@ Finally run all tests with "make config-{clang,gcc,gcc-4.8}": Release: - set YOSYS_VER to x.y.z in Makefile + - remove "bumpversion" target from Makefile - update version string in CHANGELOG git commit -am "Yosys x.y.z" @@ -115,10 +115,13 @@ LDFLAGS += -rdynamic LDLIBS += -lrt endif -YOSYS_VER := 0.8+$(shell cd $(YOSYS_SRC) && test -e .git && { git log --author=clifford@clifford.at --oneline 4d4665b.. 2> /dev/null | wc -l; }) +YOSYS_VER := 0.9+1 GIT_REV := $(shell cd $(YOSYS_SRC) && git rev-parse --short HEAD 2> /dev/null || echo UNKNOWN) OBJS = kernel/version_$(GIT_REV).o +bumpversion: + sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 8a4c6e6.. | wc -l`/;" Makefile + # set 'ABCREV = default' to use abc/ as it is # # Note: If you do ABC development, make sure that 'abc' in this directory @@ -332,6 +332,21 @@ Verilog Attributes and non-standard features that represent module parameters or localparams (when the HDL front-end is run in -pwires mode). +- The ``clkbuf_driver`` attribute can be set on an output port of a blackbox + module to mark it as a clock buffer output, and thus prevent ``clkbufmap`` + from inserting another clock buffer on a net driven by such output. + +- The ``clkbuf_sink`` attribute can be set on an input port of a module to + request clock buffer insertion by the ``clkbufmap`` pass. + +- The ``clkbuf_inhibit`` is the default attribute to set on a wire to prevent + automatic clock buffer insertion by ``clkbufmap``. This behaviour can be + overridden by providing a custom selection to ``clkbufmap``. + +- The ``iopad_external_pin`` attribute on a blackbox module's port marks + it as the external-facing pin of an I/O pad, and prevents ``iopadmap`` + from inserting another pad cell on it. + - In addition to the ``(* ... *)`` attribute syntax, Yosys supports the non-standard ``{* ... *}`` attribute syntax to set default attributes for everything that comes after the ``{* ... *}`` statement. (Reset diff --git a/examples/mimas2/run_yosys.ys b/examples/mimas2/run_yosys.ys index b3204b1ca..b48877811 100644 --- a/examples/mimas2/run_yosys.ys +++ b/examples/mimas2/run_yosys.ys @@ -1,4 +1,3 @@ read_verilog example.v -synth_xilinx -top example -family xc6s -iopadmap -bits -outpad OBUF I:O -inpad IBUF O:I +synth_xilinx -top example -family xc6s -ise write_edif -pvector bra example.edif diff --git a/misc/py_wrap_generator.py b/misc/py_wrap_generator.py index 66d661fa1..2bf364470 100644 --- a/misc/py_wrap_generator.py +++ b/misc/py_wrap_generator.py @@ -508,23 +508,17 @@ class TupleTranslator(PythonDictTranslator): #Generate c++ code to translate to a boost::python::tuple @classmethod def translate_cpp(c, varname, types, prefix, ref): - text = prefix + TupleTranslator.typename + " " + varname + "___tmp = boost::python::make_tuple(" + varname + ".first, " + varname + ".second);" - return text - tmp_name = "tmp_" + str(Translator.tmp_cntr) - Translator.tmp_cntr = Translator.tmp_cntr + 1 - if ref: - text += prefix + "for(auto " + tmp_name + " : *" + varname + ")" + # if the tuple is a pair of SigSpecs (aka SigSig), then we need + # to call get_py_obj() on each item in the tuple + if types[0].name in classnames: + first_var = types[0].name + "::get_py_obj(" + varname + ".first)" else: - text += prefix + "for(auto " + tmp_name + " : " + varname + ")" - text += prefix + "{" - if types[0].name.split(" ")[-1] in primitive_types or types[0].name in enum_names: - text += prefix + "\t" + varname + "___tmp.append(" + tmp_name + ");" - elif types[0].name in known_containers: - text += known_containers[types[0].name].translate_cpp(tmp_name, types[0].cont.args, prefix + "\t", types[1].attr_type == attr_types.star) - text += prefix + "\t" + varname + "___tmp.append(" + types[0].name + "::get_py_obj(" + tmp_name + "___tmp);" - elif types[0].name in classnames: - text += prefix + "\t" + varname + "___tmp.append(" + types[0].name + "::get_py_obj(" + tmp_name + "));" - text += prefix + "}" + first_var = varname + ".first" + if types[1].name in classnames: + second_var = types[1].name + "::get_py_obj(" + varname + ".second)" + else: + second_var = varname + ".second" + text = prefix + TupleTranslator.typename + " " + varname + "___tmp = boost::python::make_tuple(" + first_var + ", " + second_var + ");" return text #Associate the Translators with their c++ type diff --git a/passes/pmgen/README.md b/passes/pmgen/README.md index 5f6a8ab1b..0856c9ba3 100644 --- a/passes/pmgen/README.md +++ b/passes/pmgen/README.md @@ -178,6 +178,45 @@ 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`. +Slices and choices +------------------ + +Cell matches can contain "slices" and "choices". Slices can be used to +create matches for different sections of a cell. For example: + + state <int> pmux_slice + + match pmux + select pmux->type == $pmux + slice idx GetSize(port(pmux, \S)) + index <SigBit> port(pmux, \S)[idx] === port(eq, \Y) + set pmux_slice idx + endmatch + +The first argument to `slice` is the local variable name used to identify the +slice. The second argument is the number of slices that should be created for +this cell. The `set` statement can be used to copy that index into a state +variable so that later matches and/or code blocks can refer to it. + +A similar mechanism is "choices", where a list of options is given as +second argument, and the matcher will iterate over those options: + + state <SigSpec> foo bar + state <IdString> eq_ab eq_ba + + match eq + select eq->type == $eq + choice <IdString> AB {\A, \B} + define <IdString> BA (AB == \A ? \B : \A) + index <SigSpec> port(eq, AB) === foo + index <SigSpec> port(eq, BA) === bar + set eq_ab AB + set eq_ba BA + generate + +Notice how `define` can be used to define additional local variables similar +to the loop variables defined by `slice` and `choice`. + Additional code --------------- @@ -326,7 +365,7 @@ test-case generation. For example: match mul ... - generate 10 + generate 10 0 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)); @@ -335,8 +374,11 @@ test-case generation. For example: 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 first argument to `generate` is the chance of this generate block being +executed when the match block did not match anything, in percent. + +The second argument to `generate` is the chance of this generate block being +executed when the match block did match something, in percent. The special statement `finish` can be used within generate blocks to terminate the current pattern matcher run. diff --git a/passes/pmgen/pmgen.py b/passes/pmgen/pmgen.py index 18c3bf5a5..573722d68 100644 --- a/passes/pmgen/pmgen.py +++ b/passes/pmgen/pmgen.py @@ -207,9 +207,10 @@ def process_pmgfile(f, filename): state_types[current_pattern][line[1]] = "Cell*"; block["if"] = list() - block["select"] = list() + block["setup"] = list() block["index"] = list() block["filter"] = list() + block["sets"] = list() block["optional"] = False block["semioptional"] = False @@ -228,7 +229,22 @@ def process_pmgfile(f, filename): if a[0] == "select": b = l.lstrip()[6:] - block["select"].append(rewrite_cpp(b.strip())) + block["setup"].append(("select", rewrite_cpp(b.strip()))) + continue + + if a[0] == "slice": + m = re.match(r"^\s*slice\s+(\S+)\s+(.*?)\s*$", l) + block["setup"].append(("slice", m.group(1), rewrite_cpp(m.group(2)))) + continue + + if a[0] == "choice": + m = re.match(r"^\s*choice\s+<(.*?)>\s+(\S+)\s+(.*?)\s*$", l) + block["setup"].append(("choice", m.group(1), m.group(2), rewrite_cpp(m.group(3)))) + continue + + if a[0] == "define": + m = re.match(r"^\s*define\s+<(.*?)>\s+(\S+)\s+(.*?)\s*$", l) + block["setup"].append(("define", m.group(1), m.group(2), rewrite_cpp(m.group(3)))) continue if a[0] == "index": @@ -242,6 +258,11 @@ def process_pmgfile(f, filename): block["filter"].append(rewrite_cpp(b.strip())) continue + if a[0] == "set": + m = re.match(r"^\s*set\s+(\S+)\s+(.*?)\s*$", l) + block["sets"].append((m.group(1), rewrite_cpp(m.group(2)))) + continue + if a[0] == "optional": block["optional"] = True continue @@ -252,14 +273,16 @@ def process_pmgfile(f, filename): if a[0] == "generate": block["genargs"] = list([int(s) for s in a[1:]]) + if len(block["genargs"]) == 0: block["genargs"].append(100) + if len(block["genargs"]) == 1: block["genargs"].append(0) + assert len(block["genargs"]) == 2 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 + if len(a) == 1 and a[0] == "endmatch": break block["gencode"].append(rewrite_cpp(l.rstrip())) break @@ -357,8 +380,17 @@ with open(outfile, "w") as f: index_types = list() for entry in block["index"]: index_types.append(entry[0]) + value_types = ["Cell*"] + for entry in block["setup"]: + if entry[0] == "slice": + value_types.append("int") + if entry[0] == "choice": + value_types.append(entry[1]) + if entry[0] == "define": + value_types.append(entry[1]) print(" typedef std::tuple<{}> index_{}_key_type;".format(", ".join(index_types), index), file=f) - print(" dict<index_{}_key_type, vector<Cell*>> index_{};".format(index, index), file=f) + print(" typedef std::tuple<{}> index_{}_value_type;".format(", ".join(value_types), index), file=f) + print(" dict<index_{}_key_type, vector<index_{}_value_type>> index_{};".format(index, index, index), file=f) print(" dict<SigBit, pool<Cell*>> sigusers;", file=f) print(" pool<Cell*> blacklist_cells;", file=f) print(" pool<Cell*> autoremove_cells;", file=f) @@ -390,8 +422,6 @@ with open(outfile, "w") as f: print(" void add_siguser(const SigSpec &sig, Cell *cell) {", file=f) print(" for (auto bit : sigmap(sig)) {", file=f) print(" if (bit.wire == nullptr) continue;", file=f) - print(" if (sigusers.count(bit) == 0 && bit.wire->port_id)", file=f) - print(" sigusers[bit].insert(nullptr);", file=f) print(" sigusers[bit].insert(cell);", file=f) print(" }", file=f) print(" }", file=f) @@ -446,10 +476,11 @@ with open(outfile, "w") as f: else: print(" ud_{}.{} = {}();".format(current_pattern, s, t), file=f) current_pattern = None - print(" for (auto cell : module->cells()) {", file=f) + print(" for (auto port : module->ports)", file=f) + print(" add_siguser(module->wire(port), nullptr);", file=f) + print(" for (auto cell : module->cells())", file=f) print(" for (auto &conn : cell->connections())", file=f) print(" add_siguser(conn.second, cell);", file=f) - print(" }", file=f) print(" for (auto cell : cells) {", file=f) for index in range(len(blocks)): @@ -457,12 +488,34 @@ with open(outfile, "w") as f: if block["type"] == "match": print(" do {", file=f) print(" Cell *{} = cell;".format(block["cell"]), file=f) - for expr in block["select"]: - print(" if (!({})) break;".format(expr), file=f) + print(" index_{}_value_type value;".format(index), file=f) + print(" std::get<0>(value) = cell;", file=f) + loopcnt = 0 + valueidx = 1 + for item in block["setup"]: + if item[0] == "select": + print(" if (!({})) continue;".format(item[1]), file=f) + if item[0] == "slice": + print(" int &{} = std::get<{}>(value);".format(item[1], valueidx), file=f) + print(" for ({} = 0; {} < {}; {}++) {{".format(item[1], item[1], item[2], item[1]), file=f) + valueidx += 1 + loopcnt += 1 + if item[0] == "choice": + print(" vector<{}> _pmg_choices_{} = {};".format(item[1], item[2], item[3]), file=f) + print(" for (const {} &{} : _pmg_choices_{}) {{".format(item[1], item[2], item[2]), file=f) + print(" std::get<{}>(value) = {};".format(valueidx, item[2]), file=f) + valueidx += 1 + loopcnt += 1 + if item[0] == "define": + print(" {} &{} = std::get<{}>(value);".format(item[1], item[2], valueidx), file=f) + print(" {} = {};".format(item[2], item[3]), file=f) + valueidx += 1 print(" index_{}_key_type key;".format(index), file=f) for field, entry in enumerate(block["index"]): print(" std::get<{}>(key) = {};".format(field, entry[1]), file=f) - print(" index_{}[key].push_back(cell);".format(index), file=f) + print(" index_{}[key].push_back(value);".format(index), file=f) + for i in range(loopcnt): + print(" }", file=f) print(" } while (0);", file=f) print(" }", file=f) @@ -535,6 +588,8 @@ with open(outfile, "w") as f: const_st.add(s) elif blocks[i]["type"] == "match": const_st.add(blocks[i]["cell"]) + for item in blocks[i]["sets"]: + const_st.add(item[0]) else: assert False @@ -548,6 +603,10 @@ with open(outfile, "w") as f: s = block["cell"] assert s not in const_st nonconst_st.add(s) + for item in block["sets"]: + if item[0] in const_st: + const_st.remove(item[0]) + nonconst_st.add(item[0]) else: assert False @@ -570,7 +629,7 @@ with open(outfile, "w") as f: print("", file=f) for s in sorted(restore_st): t = state_types[current_pattern][s] - print(" {} backup_{} = {};".format(t, s, s), file=f) + print(" {} _pmg_backup_{} = {};".format(t, s, s), file=f) if block["type"] == "code": print("", file=f) @@ -610,7 +669,7 @@ with open(outfile, "w") as f: print("", file=f) for s in sorted(restore_st): t = state_types[current_pattern][s] - print(" {} = backup_{};".format(s, s), file=f) + print(" {} = _pmg_backup_{};".format(s, s), file=f) for s in sorted(nonconst_st): if s not in restore_st: t = state_types[current_pattern][s] @@ -622,7 +681,7 @@ 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) + print(" Cell* _pmg_backup_{} = {};".format(block["cell"], block["cell"]), file=f) if len(block["if"]): for expr in block["if"]: @@ -630,7 +689,7 @@ with open(outfile, "w") as f: print(" if (!({})) {{".format(expr), file=f) print(" {} = nullptr;".format(block["cell"]), file=f) print(" block_{}(recursion+1);".format(index+1), file=f) - print(" {} = backup_{};".format(block["cell"], block["cell"]), file=f) + print(" {} = _pmg_backup_{};".format(block["cell"], block["cell"]), file=f) print(" return;", file=f) print(" }", file=f) @@ -645,21 +704,37 @@ with open(outfile, "w") as f: print("", 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(" const vector<index_{}_value_type> &cells = cells_ptr->second;".format(index), file=f) + print(" for (int _pmg_idx = 0; _pmg_idx < GetSize(cells); _pmg_idx++) {", file=f) + print(" {} = std::get<0>(cells[_pmg_idx]);".format(block["cell"]), file=f) + valueidx = 1 + for item in block["setup"]: + if item[0] == "slice": + print(" const int &{} YS_ATTRIBUTE(unused) = std::get<{}>(cells[_pmg_idx]);".format(item[1], valueidx), file=f) + valueidx += 1 + if item[0] == "choice": + print(" const {} &{} YS_ATTRIBUTE(unused) = std::get<{}>(cells[_pmg_idx]);".format(item[1], item[2], valueidx), file=f) + valueidx += 1 + if item[0] == "define": + print(" const {} &{} YS_ATTRIBUTE(unused) = std::get<{}>(cells[_pmg_idx]);".format(item[1], item[2], valueidx), file=f) + valueidx += 1 print(" if (blacklist_cells.count({})) continue;".format(block["cell"]), file=f) for expr in block["filter"]: 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) + for item in block["sets"]: + print(" auto _pmg_backup_{} = {};".format(item[0], item[0]), file=f) + print(" {} = {};".format(item[0], item[1]), file=f) + print(" auto rollback_ptr = rollback_cache.insert(make_pair(std::get<0>(cells[_pmg_idx]), recursion));", file=f) print(" block_{}(recursion+1);".format(index+1), file=f) + for item in block["sets"]: + print(" {} = _pmg_backup_{};".format(item[0], item[0]), 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(" {} = _pmg_backup_{};".format(block["cell"], block["cell"]), file=f) print(" return;", file=f) print(" }", file=f) print(" rollback = 0;", file=f) @@ -676,13 +751,11 @@ with open(outfile, "w") as 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) + print(" {} = _pmg_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) + print(" if (generate_mode && rng(100) < (found_any_match ? {} : {})) {{".format(block["genargs"][1], block["genargs"][0]), file=f) for line in block["gencode"]: print(" " + line, file=f) print(" }", file=f) diff --git a/passes/pmgen/test_pmgen.cc b/passes/pmgen/test_pmgen.cc index 9f42a95d0..0ad769dfd 100644 --- a/passes/pmgen/test_pmgen.cc +++ b/passes/pmgen/test_pmgen.cc @@ -99,6 +99,24 @@ void reduce_tree(test_pmgen_pm &pm) log(" -> %s (%s)\n", log_id(c), log_id(c->type)); } +void opt_eqpmux(test_pmgen_pm &pm) +{ + auto &st = pm.st_eqpmux; + + SigSpec Y = st.pmux->getPort(ID::Y); + int width = GetSize(Y); + + SigSpec EQ = st.pmux->getPort(ID::B).extract(st.pmux_slice_eq*width, width); + SigSpec NE = st.pmux->getPort(ID::B).extract(st.pmux_slice_ne*width, width); + + log("Found eqpmux circuit driving %s (eq=%s, ne=%s, pmux=%s).\n", + log_signal(Y), log_id(st.eq), log_id(st.ne), log_id(st.pmux)); + + pm.autoremove(st.pmux); + Cell *c = pm.module->addMux(NEW_ID, NE, EQ, st.eq->getPort(ID::Y), Y); + 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) @@ -149,16 +167,17 @@ void generate_pattern(std::function<void(pm&,std::function<void()>)> run, const log("Generating \"%s\" patterns for pattern matcher \"%s\".\n", pattern, pmclass); int modcnt = 0; + int maxmodcnt = 100; int maxsubcnt = 4; int timeout = 0; vector<Module*> mods; - while (modcnt < 100) + while (modcnt < maxmodcnt) { int submodcnt = 0, itercnt = 0, cellcnt = 0; Module *mod = design->addModule(NEW_ID); - while (modcnt < 100 && submodcnt < maxsubcnt && itercnt++ < 1000) + while (modcnt < maxmodcnt && submodcnt < maxsubcnt && itercnt++ < 1000) { if (timeout++ > 10000) log_error("pmgen generator is stuck: 10000 iterations an no matching module generated.\n"); @@ -233,6 +252,12 @@ struct TestPmgenPass : public Pass { log("\n"); log("\n"); + log(" test_pmgen -eqpmux [options] [selection]\n"); + log("\n"); + log("Demo for recursive pmgen patterns. Optimize EQ/NE/PMUX circuits.\n"); + log("\n"); + + log("\n"); log(" test_pmgen -generate [options] <pattern_name>\n"); log("\n"); log("Create modules that match the specified pattern.\n"); @@ -277,6 +302,25 @@ struct TestPmgenPass : public Pass { test_pmgen_pm(module, module->selected_cells()).run_reduce(reduce_tree); } + void execute_eqpmux(std::vector<std::string> args, RTLIL::Design *design) + { + log_header(design, "Executing TEST_PMGEN pass (-eqpmux).\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_eqpmux(opt_eqpmux); + } + void execute_generate(std::vector<std::string> args, RTLIL::Design *design) { log_header(design, "Executing TEST_PMGEN pass (-generate).\n"); @@ -299,6 +343,9 @@ struct TestPmgenPass : public Pass { if (pattern == "reduce") return GENERATE_PATTERN(test_pmgen_pm, reduce); + if (pattern == "eqpmux") + return GENERATE_PATTERN(test_pmgen_pm, eqpmux); + if (pattern == "ice40_dsp") return GENERATE_PATTERN(ice40_dsp_pm, ice40_dsp); @@ -319,6 +366,8 @@ struct TestPmgenPass : public Pass { return execute_reduce_chain(args, design); if (args[1] == "-reduce_tree") return execute_reduce_tree(args, design); + if (args[1] == "-eqpmux") + return execute_eqpmux(args, design); if (args[1] == "-generate") return execute_generate(args, design); } diff --git a/passes/pmgen/test_pmgen.pmg b/passes/pmgen/test_pmgen.pmg index 211477a62..287ed97d8 100644 --- a/passes/pmgen/test_pmgen.pmg +++ b/passes/pmgen/test_pmgen.pmg @@ -60,8 +60,8 @@ code portname endcode match next - select nusers(port(next, \Y)) == 2 select next->type.in($_AND_, $_OR_, $_XOR_) + select nusers(port(next, \Y)) == 2 index <IdString> next->type === first->type index <SigSpec> port(next, \Y) === port(first, portname) endmatch @@ -77,8 +77,8 @@ arg first match next semioptional - select nusers(port(next, \Y)) == 2 select next->type.in($_AND_, $_OR_, $_XOR_) + select nusers(port(next, \Y)) == 2 index <IdString> next->type === chain.back().first->type index <SigSpec> port(next, \Y) === port(chain.back().first, chain.back().second) generate 10 @@ -104,3 +104,86 @@ finally if (next) chain.pop_back(); endcode + +// ================================================================== + +pattern eqpmux + +state <bool> eq_ne_signed +state <SigSpec> eq_inA eq_inB +state <int> pmux_slice_eq pmux_slice_ne + +match eq + select eq->type == $eq + choice <IdString> AB {\A, \B} + define <IdString> BA AB == \A ? \B : \A + set eq_inA port(eq, \A) + set eq_inB port(eq, \B) + set eq_ne_signed param(eq, \A_SIGNED).as_bool() +generate 100 10 + SigSpec A = module->addWire(NEW_ID, rng(7)+1); + SigSpec B = module->addWire(NEW_ID, rng(7)+1); + SigSpec Y = module->addWire(NEW_ID); + module->addEq(NEW_ID, A, B, Y, rng(2)); +endmatch + +match pmux + select pmux->type == $pmux + slice idx GetSize(port(pmux, \S)) + index <SigBit> port(pmux, \S)[idx] === port(eq, \Y) + set pmux_slice_eq idx +generate 100 10 + int width = rng(7) + 1; + int numsel = rng(4) + 1; + int idx = rng(numsel); + + SigSpec A = module->addWire(NEW_ID, width); + SigSpec Y = module->addWire(NEW_ID, width); + + SigSpec B, S; + for (int i = 0; i < numsel; i++) { + B.append(module->addWire(NEW_ID, width)); + S.append(i == idx ? port(eq, \Y) : module->addWire(NEW_ID)); + } + + module->addPmux(NEW_ID, A, B, S, Y); +endmatch + +match ne + select ne->type == $ne + choice <IdString> AB {\A, \B} + define <IdString> BA (AB == \A ? \B : \A) + index <SigSpec> port(ne, AB) === eq_inA + index <SigSpec> port(ne, BA) === eq_inB + index <int> param(ne, \A_SIGNED).as_bool() === eq_ne_signed +generate 100 10 + SigSpec A = eq_inA, B = eq_inB, Y; + if (rng(2)) { + std::swap(A, B); + } + if (rng(2)) { + for (auto bit : port(pmux, \S)) { + if (nusers(bit) < 2) + Y.append(bit); + } + if (GetSize(Y)) + Y = Y[rng(GetSize(Y))]; + else + Y = module->addWire(NEW_ID); + } else { + Y = module->addWire(NEW_ID); + } + module->addNe(NEW_ID, A, B, Y, rng(2)); +endmatch + +match pmux2 + select pmux2->type == $pmux + slice idx GetSize(port(pmux2, \S)) + index <Cell*> pmux2 === pmux + index <SigBit> port(pmux2, \S)[idx] === port(ne, \Y) + set pmux_slice_ne idx +endmatch + +code + accept; +endcode diff --git a/passes/sat/async2sync.cc b/passes/sat/async2sync.cc index d045d0dcb..24ae6e448 100644 --- a/passes/sat/async2sync.cc +++ b/passes/sat/async2sync.cc @@ -39,7 +39,7 @@ struct Async2syncPass : public Pass { log("reset value in the next cycle regardless of the data-in value at the time of\n"); log("the clock edge.\n"); log("\n"); - log("Currently only $adff and $dffsr cells are supported by this pass.\n"); + log("Currently only $adff, $dffsr, and $dlatch cells are supported by this pass.\n"); log("\n"); } void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE @@ -169,6 +169,41 @@ struct Async2syncPass : public Pass { cell->type = "$dff"; continue; } + + if (cell->type.in("$dlatch")) + { + bool en_pol = cell->parameters["\\EN_POLARITY"].as_bool(); + + SigSpec sig_en = cell->getPort("\\EN"); + SigSpec sig_d = cell->getPort("\\D"); + SigSpec sig_q = cell->getPort("\\Q"); + + log("Replacing %s.%s (%s): EN=%s, D=%s, Q=%s\n", + log_id(module), log_id(cell), log_id(cell->type), + log_signal(sig_en), log_signal(sig_d), log_signal(sig_q)); + + Const init_val; + for (int i = 0; i < GetSize(sig_q); i++) { + SigBit bit = sigmap(sig_q[i]); + init_val.bits.push_back(initbits.count(bit) ? initbits.at(bit) : State::Sx); + del_initbits.insert(bit); + } + + Wire *new_q = module->addWire(NEW_ID, GetSize(sig_q)); + new_q->attributes["\\init"] = init_val; + + if (en_pol) { + module->addMux(NEW_ID, new_q, sig_d, sig_en, sig_q); + } else { + module->addMux(NEW_ID, sig_d, new_q, sig_en, sig_q); + } + + cell->setPort("\\Q", new_q); + cell->unsetPort("\\EN"); + cell->unsetParam("\\EN_POLARITY"); + cell->type = "$ff"; + continue; + } } for (auto wire : module->wires()) diff --git a/passes/sat/sat.cc b/passes/sat/sat.cc index dd56d8c71..430bba1e8 100644 --- a/passes/sat/sat.cc +++ b/passes/sat/sat.cc @@ -268,7 +268,7 @@ struct SatHelper RTLIL::SigSpec removed_bits; for (int i = 0; i < lhs.size(); i++) { RTLIL::SigSpec bit = lhs.extract(i, 1); - if (!satgen.initial_state.check_all(bit)) { + if (rhs[i] == State::Sx || !satgen.initial_state.check_all(bit)) { removed_bits.append(bit); lhs.remove(i, 1); rhs.remove(i, 1); diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc index 56f05eca4..631a80aa5 100644 --- a/passes/techmap/Makefile.inc +++ b/passes/techmap/Makefile.inc @@ -16,6 +16,7 @@ endif ifneq ($(SMALL),1) OBJS += passes/techmap/iopadmap.o +OBJS += passes/techmap/clkbufmap.o OBJS += passes/techmap/hilomap.o OBJS += passes/techmap/extract.o OBJS += passes/techmap/extract_fa.o diff --git a/passes/techmap/attrmap.cc b/passes/techmap/attrmap.cc index a38638e0b..5f30817d4 100644 --- a/passes/techmap/attrmap.cc +++ b/passes/techmap/attrmap.cc @@ -143,6 +143,82 @@ void attrmap_apply(string objname, vector<std::unique_ptr<AttrmapAction>> &actio attributes.swap(new_attributes); } +void log_attrmap_paramap_options() +{ + log(" -tocase <name>\n"); + log(" Match attribute names case-insensitively and set it to the specified\n"); + log(" name.\n"); + log("\n"); + log(" -rename <old_name> <new_name>\n"); + log(" Rename attributes as specified\n"); + log("\n"); + log(" -map <old_name>=<old_value> <new_name>=<new_value>\n"); + log(" Map key/value pairs as indicated.\n"); + log("\n"); + log(" -imap <old_name>=<old_value> <new_name>=<new_value>\n"); + log(" Like -map, but use case-insensitive match for <old_value> when\n"); + log(" it is a string value.\n"); + log("\n"); + log(" -remove <name>=<value>\n"); + log(" Remove attributes matching this pattern.\n"); +} + +bool parse_attrmap_paramap_options(size_t &argidx, std::vector<std::string> &args, vector<std::unique_ptr<AttrmapAction>> &actions) +{ + std::string arg = args[argidx]; + if (arg == "-tocase" && argidx+1 < args.size()) { + auto action = new AttrmapTocase; + action->name = args[++argidx]; + actions.push_back(std::unique_ptr<AttrmapAction>(action)); + return true; + } + if (arg == "-rename" && argidx+2 < args.size()) { + auto action = new AttrmapRename; + action->old_name = args[++argidx]; + action->new_name = args[++argidx]; + actions.push_back(std::unique_ptr<AttrmapAction>(action)); + return true; + } + if ((arg == "-map" || arg == "-imap") && argidx+2 < args.size()) { + string arg1 = args[++argidx]; + string arg2 = args[++argidx]; + string val1, val2; + size_t p = arg1.find("="); + if (p != string::npos) { + val1 = arg1.substr(p+1); + arg1 = arg1.substr(0, p); + } + p = arg2.find("="); + if (p != string::npos) { + val2 = arg2.substr(p+1); + arg2 = arg2.substr(0, p); + } + auto action = new AttrmapMap; + action->imap = (arg == "-map"); + action->old_name = arg1; + action->new_name = arg2; + action->old_value = val1; + action->new_value = val2; + actions.push_back(std::unique_ptr<AttrmapAction>(action)); + return true; + } + if (arg == "-remove" && argidx+1 < args.size()) { + string arg1 = args[++argidx], val1; + size_t p = arg1.find("="); + if (p != string::npos) { + val1 = arg1.substr(p+1); + arg1 = arg1.substr(0, p); + } + auto action = new AttrmapRemove; + action->name = arg1; + action->has_value = (p != string::npos); + action->value = val1; + actions.push_back(std::unique_ptr<AttrmapAction>(action)); + return true; + } + return false; +} + struct AttrmapPass : public Pass { AttrmapPass() : Pass("attrmap", "renaming attributes") { } void help() YS_OVERRIDE @@ -151,25 +227,10 @@ struct AttrmapPass : public Pass { log("\n"); log(" attrmap [options] [selection]\n"); log("\n"); - log("This command renames attributes and/or mapps key/value pairs to\n"); + log("This command renames attributes and/or maps key/value pairs to\n"); log("other key/value pairs.\n"); log("\n"); - log(" -tocase <name>\n"); - log(" Match attribute names case-insensitively and set it to the specified\n"); - log(" name.\n"); - log("\n"); - log(" -rename <old_name> <new_name>\n"); - log(" Rename attributes as specified\n"); - log("\n"); - log(" -map <old_name>=<old_value> <new_name>=<new_value>\n"); - log(" Map key/value pairs as indicated.\n"); - log("\n"); - log(" -imap <old_name>=<old_value> <new_name>=<new_value>\n"); - log(" Like -map, but use case-insensitive match for <old_value> when\n"); - log(" it is a string value.\n"); - log("\n"); - log(" -remove <name>=<value>\n"); - log(" Remove attributes matching this pattern.\n"); + log_attrmap_paramap_options(); log("\n"); log(" -modattr\n"); log(" Operate on module attributes instead of attributes on wires and cells.\n"); @@ -190,58 +251,9 @@ struct AttrmapPass : public Pass { size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { - std::string arg = args[argidx]; - if (arg == "-tocase" && argidx+1 < args.size()) { - auto action = new AttrmapTocase; - action->name = args[++argidx]; - actions.push_back(std::unique_ptr<AttrmapAction>(action)); - continue; - } - if (arg == "-rename" && argidx+2 < args.size()) { - auto action = new AttrmapRename; - action->old_name = args[++argidx]; - action->new_name = args[++argidx]; - actions.push_back(std::unique_ptr<AttrmapAction>(action)); + if (parse_attrmap_paramap_options(argidx, args, actions)) continue; - } - if ((arg == "-map" || arg == "-imap") && argidx+2 < args.size()) { - string arg1 = args[++argidx]; - string arg2 = args[++argidx]; - string val1, val2; - size_t p = arg1.find("="); - if (p != string::npos) { - val1 = arg1.substr(p+1); - arg1 = arg1.substr(0, p); - } - p = arg2.find("="); - if (p != string::npos) { - val2 = arg2.substr(p+1); - arg2 = arg2.substr(0, p); - } - auto action = new AttrmapMap; - action->imap = (arg == "-map"); - action->old_name = arg1; - action->new_name = arg2; - action->old_value = val1; - action->new_value = val2; - actions.push_back(std::unique_ptr<AttrmapAction>(action)); - continue; - } - if (arg == "-remove" && argidx+1 < args.size()) { - string arg1 = args[++argidx], val1; - size_t p = arg1.find("="); - if (p != string::npos) { - val1 = arg1.substr(p+1); - arg1 = arg1.substr(0, p); - } - auto action = new AttrmapRemove; - action->name = arg1; - action->has_value = (p != string::npos); - action->value = val1; - actions.push_back(std::unique_ptr<AttrmapAction>(action)); - continue; - } - if (arg == "-modattr") { + if (args[argidx] == "-modattr") { modattr_mode = true; continue; } @@ -287,4 +299,43 @@ struct AttrmapPass : public Pass { } } AttrmapPass; +struct ParamapPass : public Pass { + ParamapPass() : Pass("paramap", "renaming cell parameters") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" paramap [options] [selection]\n"); + log("\n"); + log("This command renames cell parameters and/or maps key/value pairs to\n"); + log("other key/value pairs.\n"); + log("\n"); + log_attrmap_paramap_options(); + log("\n"); + log("For example, mapping Diamond-style ECP5 \"init\" attributes to Yosys-style:\n"); + log("\n"); + log(" paramap -tocase INIT t:LUT4\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing PARAMAP pass (move or copy cell parameters).\n"); + + vector<std::unique_ptr<AttrmapAction>> actions; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (parse_attrmap_paramap_options(argidx, args, actions)) + continue; + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + for (auto cell : module->selected_cells()) + attrmap_apply(stringf("%s.%s", log_id(module), log_id(cell)), actions, cell->parameters); + } +} ParamapPass; + PRIVATE_NAMESPACE_END diff --git a/passes/techmap/clkbufmap.cc b/passes/techmap/clkbufmap.cc new file mode 100644 index 000000000..246932d81 --- /dev/null +++ b/passes/techmap/clkbufmap.cc @@ -0,0 +1,298 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2019 Marcin Kościelnicki <mwk@0x04.net> + * + * 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 + +void split_portname_pair(std::string &port1, std::string &port2) +{ + size_t pos = port1.find_first_of(':'); + if (pos != std::string::npos) { + port2 = port1.substr(pos+1); + port1 = port1.substr(0, pos); + } +} + +struct ClkbufmapPass : public Pass { + ClkbufmapPass() : Pass("clkbufmap", "insert global buffers on clock networks") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" clkbufmap [options] [selection]\n"); + log("\n"); + log("Inserts global buffers between nets connected to clock inputs and their drivers.\n"); + log("\n"); + log("In the absence of any selection, all wires without the 'clkbuf_inhibit'\n"); + log("attribute will be considered for global buffer insertion.\n"); + log("Alternatively, to consider all wires without the 'buffer_type' attribute set to\n"); + log("'none' or 'bufr' one would specify:\n"); + log(" 'w:* a:buffer_type=none a:buffer_type=bufr %%u %%d'\n"); + log("as the selection.\n"); + log("\n"); + log(" -buf <celltype> <portname_out>:<portname_in>\n"); + log(" Specifies the cell type to use for the global buffers\n"); + log(" and its port names. The first port will be connected to\n"); + log(" the clock network sinks, and the second will be connected\n"); + log(" to the actual clock source. This option is required.\n"); + log("\n"); + log(" -inpad <celltype> <portname_out>:<portname_in>\n"); + log(" If specified, a PAD cell of the given type is inserted on\n"); + log(" clock nets that are also top module's inputs (in addition\n"); + log(" to the global buffer).\n"); + log("\n"); + } + + void module_queue(Design *design, Module *module, std::vector<Module *> &modules_sorted, pool<Module *> &modules_processed) { + if (modules_processed.count(module)) + return; + for (auto cell : module->cells()) { + Module *submodule = design->module(cell->type); + if (!submodule) + continue; + module_queue(design, submodule, modules_sorted, modules_processed); + } + modules_sorted.push_back(module); + modules_processed.insert(module); + } + + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing CLKBUFMAP pass (inserting global clock buffers).\n"); + + std::string buf_celltype, buf_portname, buf_portname2; + std::string inpad_celltype, inpad_portname, inpad_portname2; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + std::string arg = args[argidx]; + if (arg == "-buf" && argidx+2 < args.size()) { + buf_celltype = args[++argidx]; + buf_portname = args[++argidx]; + split_portname_pair(buf_portname, buf_portname2); + continue; + } + if (arg == "-inpad" && argidx+2 < args.size()) { + inpad_celltype = args[++argidx]; + inpad_portname = args[++argidx]; + split_portname_pair(inpad_portname, inpad_portname2); + continue; + } + break; + } + + bool select = false; + if (argidx < args.size()) { + if (args[argidx].compare(0, 1, "-") != 0) + select = true; + extra_args(args, argidx, design); + } + + if (buf_celltype.empty()) + log_error("The -buf option is required.\n"); + + // Cell type, port name, bit index. + pool<pair<IdString, pair<IdString, int>>> sink_ports; + pool<pair<IdString, pair<IdString, int>>> buf_ports; + + // Process submodules before module using them. + std::vector<Module *> modules_sorted; + pool<Module *> modules_processed; + for (auto module : design->selected_modules()) + module_queue(design, module, modules_sorted, modules_processed); + + for (auto module : modules_sorted) + { + if (module->get_blackbox_attribute()) { + for (auto port : module->ports) { + auto wire = module->wire(port); + if (wire->get_bool_attribute("\\clkbuf_driver")) + for (int i = 0; i < GetSize(wire); i++) + buf_ports.insert(make_pair(module->name, make_pair(wire->name, i))); + if (wire->get_bool_attribute("\\clkbuf_sink")) + for (int i = 0; i < GetSize(wire); i++) + sink_ports.insert(make_pair(module->name, make_pair(wire->name, i))); + } + continue; + } + pool<SigBit> sink_wire_bits; + pool<SigBit> buf_wire_bits; + pool<SigBit> driven_wire_bits; + SigMap sigmap(module); + // bit -> (buffer, buffer's input) + dict<SigBit, pair<Cell *, Wire *>> buffered_bits; + + // First, collect nets that could use a clock buffer. + for (auto cell : module->cells()) + for (auto port : cell->connections()) + for (int i = 0; i < port.second.size(); i++) + if (sink_ports.count(make_pair(cell->type, make_pair(port.first, i)))) + sink_wire_bits.insert(sigmap(port.second[i])); + + // Second, collect ones that already have a clock buffer. + for (auto cell : module->cells()) + for (auto port : cell->connections()) + for (int i = 0; i < port.second.size(); i++) + if (buf_ports.count(make_pair(cell->type, make_pair(port.first, i)))) + buf_wire_bits.insert(sigmap(port.second[i])); + + // Collect all driven bits. + for (auto cell : module->cells()) + for (auto port : cell->connections()) + if (cell->output(port.first)) + for (int i = 0; i < port.second.size(); i++) + driven_wire_bits.insert(port.second[i]); + + // Insert buffers. + std::vector<pair<Wire *, Wire *>> input_queue; + // Copy current wire list, as we will be adding new ones during iteration. + std::vector<Wire *> wires(module->wires()); + for (auto wire : wires) + { + // Should not happen. + if (wire->port_input && wire->port_output) + continue; + bool process_wire = module->selected(wire); + if (!select && wire->get_bool_attribute("\\clkbuf_inhibit")) + process_wire = false; + if (!process_wire) { + // This wire is supposed to be bypassed, so make sure we don't buffer it in + // some buffer higher up in the hierarchy. + if (wire->port_output) + for (int i = 0; i < GetSize(wire); i++) + buf_ports.insert(make_pair(module->name, make_pair(wire->name, i))); + continue; + } + + pool<int> input_bits; + + for (int i = 0; i < GetSize(wire); i++) + { + SigBit wire_bit(wire, i); + SigBit mapped_wire_bit = sigmap(wire_bit); + if (buf_wire_bits.count(mapped_wire_bit)) { + // Already buffered downstream. If this is an output, mark it. + if (wire->port_output) + buf_ports.insert(make_pair(module->name, make_pair(wire->name, i))); + } else if (!sink_wire_bits.count(mapped_wire_bit)) { + // Nothing to do. + } else if (driven_wire_bits.count(wire_bit) || (wire->port_input && module->get_bool_attribute("\\top"))) { + // Clock network not yet buffered, driven by one of + // our cells or a top-level input -- buffer it. + + log("Inserting %s on %s.%s[%d].\n", buf_celltype.c_str(), log_id(module), log_id(wire), i); + RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(buf_celltype)); + Wire *iwire = module->addWire(NEW_ID); + cell->setPort(RTLIL::escape_id(buf_portname), mapped_wire_bit); + cell->setPort(RTLIL::escape_id(buf_portname2), iwire); + if (wire->port_input && !inpad_celltype.empty() && module->get_bool_attribute("\\top")) { + log("Inserting %s on %s.%s[%d].\n", inpad_celltype.c_str(), log_id(module), log_id(wire), i); + RTLIL::Cell *cell2 = module->addCell(NEW_ID, RTLIL::escape_id(inpad_celltype)); + cell2->setPort(RTLIL::escape_id(inpad_portname), iwire); + iwire = module->addWire(NEW_ID); + cell2->setPort(RTLIL::escape_id(inpad_portname2), iwire); + } + buffered_bits[mapped_wire_bit] = make_pair(cell, iwire); + + if (wire->port_input) { + input_bits.insert(i); + } + } else if (wire->port_input) { + // A clock input in a submodule -- mark it, let higher level + // worry about it. + if (wire->port_input) + sink_ports.insert(make_pair(module->name, make_pair(wire->name, i))); + } + } + if (!input_bits.empty()) { + // This is an input port and some buffers were inserted -- we need + // to create a new input wire and transfer attributes. + Wire *new_wire = module->addWire(NEW_ID, wire); + + for (int i = 0; i < wire->width; i++) { + SigBit wire_bit(wire, i); + SigBit mapped_wire_bit = sigmap(wire_bit); + auto it = buffered_bits.find(mapped_wire_bit); + if (it != buffered_bits.end()) { + + module->connect(it->second.second, SigSpec(new_wire, i)); + } else { + module->connect(SigSpec(wire, i), SigSpec(new_wire, i)); + } + } + input_queue.push_back(make_pair(wire, new_wire)); + } + } + + // Mark any newly-buffered output ports as such. + for (auto wire : module->selected_wires()) { + if (wire->port_input || !wire->port_output) + continue; + for (int i = 0; i < GetSize(wire); i++) + { + SigBit wire_bit(wire, i); + SigBit mapped_wire_bit = sigmap(wire_bit); + if (buffered_bits.count(mapped_wire_bit)) + buf_ports.insert(make_pair(module->name, make_pair(wire->name, i))); + } + } + + // Reconnect the drivers to buffer inputs. + for (auto cell : module->cells()) + for (auto port : cell->connections()) { + if (!cell->output(port.first)) + continue; + SigSpec sig = port.second; + bool newsig = false; + for (auto &bit : sig) { + const auto it = buffered_bits.find(sigmap(bit)); + if (it == buffered_bits.end()) + continue; + // Avoid substituting buffer's own output pin. + if (cell == it->second.first) + continue; + bit = it->second.second; + newsig = true; + } + if (newsig) + cell->setPort(port.first, sig); + } + + // This has to be done last, to avoid upsetting sigmap before the port reconnections. + for (auto &it : input_queue) { + Wire *wire = it.first; + Wire *new_wire = it.second; + module->swap_names(new_wire, wire); + wire->attributes.clear(); + wire->port_id = 0; + wire->port_input = false; + wire->port_output = false; + } + + module->fixup_ports(); + } + } +} ClkbufmapPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/iopadmap.cc b/passes/techmap/iopadmap.cc index a2551316f..c868b9a87 100644 --- a/passes/techmap/iopadmap.cc +++ b/passes/techmap/iopadmap.cc @@ -64,6 +64,11 @@ struct IopadmapPass : public Pass { log(" of the tristate driver and the 2nd portname is the internal output\n"); log(" buffering the external signal.\n"); log("\n"); + log(" -ignore <celltype> <portname>[:<portname>]*\n"); + log(" Skips mapping inputs/outputs that are already connected to given\n"); + log(" ports of the given cell. Can be used multiple times. This is in\n"); + log(" addition to the cells specified as mapping targets.\n"); + log("\n"); log(" -widthparam <param_name>\n"); log(" Use the specified parameter name to set the port width.\n"); log("\n"); @@ -88,6 +93,7 @@ struct IopadmapPass : public Pass { std::string toutpad_celltype, toutpad_portname, toutpad_portname2, toutpad_portname3; std::string tinoutpad_celltype, tinoutpad_portname, tinoutpad_portname2, tinoutpad_portname3, tinoutpad_portname4; std::string widthparam, nameparam; + pool<pair<IdString, IdString>> ignore; bool flag_bits = false; size_t argidx; @@ -127,6 +133,18 @@ struct IopadmapPass : public Pass { split_portname_pair(tinoutpad_portname3, tinoutpad_portname4); continue; } + if (arg == "-ignore" && argidx+2 < args.size()) { + std::string ignore_celltype = args[++argidx]; + std::string ignore_portname = args[++argidx]; + std::string ignore_portname2; + while (!ignore_portname.empty()) { + split_portname_pair(ignore_portname, ignore_portname2); + ignore.insert(make_pair(RTLIL::escape_id(ignore_celltype), RTLIL::escape_id(ignore_portname))); + + ignore_portname = ignore_portname2; + } + continue; + } if (arg == "-widthparam" && argidx+1 < args.size()) { widthparam = args[++argidx]; continue; @@ -143,6 +161,23 @@ struct IopadmapPass : public Pass { } extra_args(args, argidx, design); + if (!inpad_portname2.empty()) + ignore.insert(make_pair(RTLIL::escape_id(inpad_celltype), RTLIL::escape_id(inpad_portname2))); + if (!outpad_portname2.empty()) + ignore.insert(make_pair(RTLIL::escape_id(outpad_celltype), RTLIL::escape_id(outpad_portname2))); + if (!inoutpad_portname2.empty()) + ignore.insert(make_pair(RTLIL::escape_id(inoutpad_celltype), RTLIL::escape_id(inoutpad_portname2))); + if (!toutpad_portname3.empty()) + ignore.insert(make_pair(RTLIL::escape_id(toutpad_celltype), RTLIL::escape_id(toutpad_portname3))); + if (!tinoutpad_portname4.empty()) + ignore.insert(make_pair(RTLIL::escape_id(tinoutpad_celltype), RTLIL::escape_id(tinoutpad_portname4))); + + for (auto module : design->modules()) + if (module->get_blackbox_attribute()) + for (auto wire : module->wires()) + if (wire->get_bool_attribute("\\iopad_external_pin")) + ignore.insert(make_pair(module->name, wire->name)); + for (auto module : design->selected_modules()) { dict<IdString, pool<int>> skip_wires; @@ -150,28 +185,11 @@ struct IopadmapPass : public Pass { SigMap sigmap(module); for (auto cell : module->cells()) - { - if (cell->type == RTLIL::escape_id(inpad_celltype) && cell->hasPort(RTLIL::escape_id(inpad_portname2))) - for (auto bit : sigmap(cell->getPort(RTLIL::escape_id(inpad_portname2)))) - skip_wire_bits.insert(bit); - - if (cell->type == RTLIL::escape_id(outpad_celltype) && cell->hasPort(RTLIL::escape_id(outpad_portname2))) - for (auto bit : sigmap(cell->getPort(RTLIL::escape_id(outpad_portname2)))) + for (auto port : cell->connections()) + if (ignore.count(make_pair(cell->type, port.first))) + for (auto bit : sigmap(port.second)) skip_wire_bits.insert(bit); - if (cell->type == RTLIL::escape_id(inoutpad_celltype) && cell->hasPort(RTLIL::escape_id(inoutpad_portname2))) - for (auto bit : sigmap(cell->getPort(RTLIL::escape_id(inoutpad_portname2)))) - skip_wire_bits.insert(bit); - - if (cell->type == RTLIL::escape_id(toutpad_celltype) && cell->hasPort(RTLIL::escape_id(toutpad_portname3))) - for (auto bit : sigmap(cell->getPort(RTLIL::escape_id(toutpad_portname3)))) - skip_wire_bits.insert(bit); - - if (cell->type == RTLIL::escape_id(tinoutpad_celltype) && cell->hasPort(RTLIL::escape_id(tinoutpad_portname4))) - for (auto bit : sigmap(cell->getPort(RTLIL::escape_id(tinoutpad_portname4)))) - skip_wire_bits.insert(bit); - } - if (!toutpad_celltype.empty() || !tinoutpad_celltype.empty()) { dict<SigBit, pair<IdString, pool<IdString>>> tbuf_bits; diff --git a/techlibs/ecp5/Makefile.inc b/techlibs/ecp5/Makefile.inc index cffd5c3a4..c02ee677f 100644 --- a/techlibs/ecp5/Makefile.inc +++ b/techlibs/ecp5/Makefile.inc @@ -1,5 +1,6 @@ -OBJS += techlibs/ecp5/synth_ecp5.o techlibs/ecp5/ecp5_ffinit.o +OBJS += techlibs/ecp5/synth_ecp5.o techlibs/ecp5/ecp5_ffinit.o \ + techlibs/ecp5/ecp5_gsr.o $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/cells_map.v)) $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/cells_sim.v)) diff --git a/techlibs/ecp5/brams_map.v b/techlibs/ecp5/brams_map.v index b2c136863..0353cbadb 100644 --- a/techlibs/ecp5/brams_map.v +++ b/techlibs/ecp5/brams_map.v @@ -33,7 +33,7 @@ module \$__ECP5_DP16KD (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN); .CLKBMUX(CLKBMUX), .WRITEMODE_A(WRITEMODE_A), .WRITEMODE_B("READBEFOREWRITE"), - .GSR("DISABLED") + .GSR("AUTO") ) _TECHMAP_REPLACE_ ( `include "bram_conn_1.vh" .CLKA(CLK2), .CLKB(CLK3), @@ -50,7 +50,7 @@ module \$__ECP5_DP16KD (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN); .CLKBMUX(CLKBMUX), .WRITEMODE_A(WRITEMODE_A), .WRITEMODE_B("READBEFOREWRITE"), - .GSR("DISABLED") + .GSR("AUTO") ) _TECHMAP_REPLACE_ ( `include "bram_conn_2.vh" .CLKA(CLK2), .CLKB(CLK3), @@ -67,7 +67,7 @@ module \$__ECP5_DP16KD (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN); .CLKBMUX(CLKBMUX), .WRITEMODE_A(WRITEMODE_A), .WRITEMODE_B("READBEFOREWRITE"), - .GSR("DISABLED") + .GSR("AUTO") ) _TECHMAP_REPLACE_ ( `include "bram_conn_4.vh" .CLKA(CLK2), .CLKB(CLK3), @@ -84,7 +84,7 @@ module \$__ECP5_DP16KD (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN); .CLKBMUX(CLKBMUX), .WRITEMODE_A(WRITEMODE_A), .WRITEMODE_B("READBEFOREWRITE"), - .GSR("DISABLED") + .GSR("AUTO") ) _TECHMAP_REPLACE_ ( `include "bram_conn_9.vh" .CLKA(CLK2), .CLKB(CLK3), @@ -101,7 +101,7 @@ module \$__ECP5_DP16KD (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN); .CLKBMUX(CLKBMUX), .WRITEMODE_A(WRITEMODE_A), .WRITEMODE_B("READBEFOREWRITE"), - .GSR("DISABLED") + .GSR("AUTO") ) _TECHMAP_REPLACE_ ( `include "bram_conn_18.vh" .CLKA(CLK2), .CLKB(CLK3), diff --git a/techlibs/ecp5/cells_bb.v b/techlibs/ecp5/cells_bb.v index 223e19b9e..8557053b6 100644 --- a/techlibs/ecp5/cells_bb.v +++ b/techlibs/ecp5/cells_bb.v @@ -664,3 +664,23 @@ module PCSCLKDIV ( ); parameter GSR = "DISABLED"; endmodule + +// Note: this module is not marked keep as we want it swept away in synth (sim use only) +(* blackbox *) +module PUR ( + input PUR +); + parameter RST_PULSE = 1; +endmodule + +(* blackbox, keep *) +module GSR ( + input GSR +); +endmodule + +(* blackbox, keep *) +module SGSR ( + input GSR, CLK +); +endmodule
\ No newline at end of file diff --git a/techlibs/ecp5/cells_map.v b/techlibs/ecp5/cells_map.v index 6985fbbc8..0a92d906d 100644 --- a/techlibs/ecp5/cells_map.v +++ b/techlibs/ecp5/cells_map.v @@ -1,51 +1,51 @@ -module \$_DFF_N_ (input D, C, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("RESET")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(1'b0), .DI(D), .Q(Q)); endmodule -module \$_DFF_P_ (input D, C, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(1'b0), .DI(D), .Q(Q)); endmodule - -module \$_DFFE_NN_ (input D, C, E, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("INV"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("RESET")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(1'b0), .DI(D), .Q(Q)); endmodule -module \$_DFFE_PN_ (input D, C, E, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("INV"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(1'b0), .DI(D), .Q(Q)); endmodule - -module \$_DFFE_NP_ (input D, C, E, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("RESET")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(1'b0), .DI(D), .Q(Q)); endmodule -module \$_DFFE_PP_ (input D, C, E, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(1'b0), .DI(D), .Q(Q)); endmodule - -module \$_DFF_NN0_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(!R), .DI(D), .Q(Q)); endmodule -module \$_DFF_NN1_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(!R), .DI(D), .Q(Q)); endmodule -module \$_DFF_PN0_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(!R), .DI(D), .Q(Q)); endmodule -module \$_DFF_PN1_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(!R), .DI(D), .Q(Q)); endmodule - -module \$_DFF_NP0_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(R), .DI(D), .Q(Q)); endmodule -module \$_DFF_NP1_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(R), .DI(D), .Q(Q)); endmodule -module \$_DFF_PP0_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(R), .DI(D), .Q(Q)); endmodule -module \$_DFF_PP1_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(R), .DI(D), .Q(Q)); endmodule - -module \$__DFFS_NN0_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(!R), .DI(D), .Q(Q)); endmodule -module \$__DFFS_NN1_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(!R), .DI(D), .Q(Q)); endmodule -module \$__DFFS_PN0_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(!R), .DI(D), .Q(Q)); endmodule -module \$__DFFS_PN1_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(!R), .DI(D), .Q(Q)); endmodule - -module \$__DFFS_NP0_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(R), .DI(D), .Q(Q)); endmodule -module \$__DFFS_NP1_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(R), .DI(D), .Q(Q)); endmodule -module \$__DFFS_PP0_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(R), .DI(D), .Q(Q)); endmodule -module \$__DFFS_PP1_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(R), .DI(D), .Q(Q)); endmodule - -module \$__DFFE_NN0 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(!R), .DI(D), .Q(Q)); endmodule -module \$__DFFE_NN1 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(!R), .DI(D), .Q(Q)); endmodule -module \$__DFFE_PN0 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(!R), .DI(D), .Q(Q)); endmodule -module \$__DFFE_PN1 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(!R), .DI(D), .Q(Q)); endmodule - -module \$__DFFE_NP0 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(R), .DI(D), .Q(Q)); endmodule -module \$__DFFE_NP1 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(R), .DI(D), .Q(Q)); endmodule -module \$__DFFE_PP0 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(R), .DI(D), .Q(Q)); endmodule -module \$__DFFE_PP1 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(R), .DI(D), .Q(Q)); endmodule - -module \$__DFFSE_NN0 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(!R), .DI(D), .Q(Q)); endmodule -module \$__DFFSE_NN1 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(!R), .DI(D), .Q(Q)); endmodule -module \$__DFFSE_PN0 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(!R), .DI(D), .Q(Q)); endmodule -module \$__DFFSE_PN1 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(!R), .DI(D), .Q(Q)); endmodule - -module \$__DFFSE_NP0 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(R), .DI(D), .Q(Q)); endmodule -module \$__DFFSE_NP1 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(R), .DI(D), .Q(Q)); endmodule -module \$__DFFSE_PP0 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(R), .DI(D), .Q(Q)); endmodule -module \$__DFFSE_PP1 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(R), .DI(D), .Q(Q)); endmodule +module \$_DFF_N_ (input D, C, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("1"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("RESET")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(1'b0), .DI(D), .Q(Q)); endmodule +module \$_DFF_P_ (input D, C, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(1'b0), .DI(D), .Q(Q)); endmodule + +module \$_DFFE_NN_ (input D, C, E, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("INV"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("RESET")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(1'b0), .DI(D), .Q(Q)); endmodule +module \$_DFFE_PN_ (input D, C, E, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("INV"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(1'b0), .DI(D), .Q(Q)); endmodule + +module \$_DFFE_NP_ (input D, C, E, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("CE"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("RESET")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(1'b0), .DI(D), .Q(Q)); endmodule +module \$_DFFE_PP_ (input D, C, E, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(1'b0), .DI(D), .Q(Q)); endmodule + +module \$_DFF_NN0_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("1"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(!R), .DI(D), .Q(Q)); endmodule +module \$_DFF_NN1_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("1"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(!R), .DI(D), .Q(Q)); endmodule +module \$_DFF_PN0_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(!R), .DI(D), .Q(Q)); endmodule +module \$_DFF_PN1_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(!R), .DI(D), .Q(Q)); endmodule + +module \$_DFF_NP0_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("1"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(R), .DI(D), .Q(Q)); endmodule +module \$_DFF_NP1_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("1"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(R), .DI(D), .Q(Q)); endmodule +module \$_DFF_PP0_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(R), .DI(D), .Q(Q)); endmodule +module \$_DFF_PP1_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(R), .DI(D), .Q(Q)); endmodule + +module \$__DFFS_NN0_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("1"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(!R), .DI(D), .Q(Q)); endmodule +module \$__DFFS_NN1_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("1"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(!R), .DI(D), .Q(Q)); endmodule +module \$__DFFS_PN0_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(!R), .DI(D), .Q(Q)); endmodule +module \$__DFFS_PN1_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(!R), .DI(D), .Q(Q)); endmodule + +module \$__DFFS_NP0_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("1"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(R), .DI(D), .Q(Q)); endmodule +module \$__DFFS_NP1_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("1"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(R), .DI(D), .Q(Q)); endmodule +module \$__DFFS_PP0_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(R), .DI(D), .Q(Q)); endmodule +module \$__DFFS_PP1_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(R), .DI(D), .Q(Q)); endmodule + +module \$__DFFE_NN0 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("CE"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(!R), .DI(D), .Q(Q)); endmodule +module \$__DFFE_NN1 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("CE"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(!R), .DI(D), .Q(Q)); endmodule +module \$__DFFE_PN0 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(!R), .DI(D), .Q(Q)); endmodule +module \$__DFFE_PN1 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(!R), .DI(D), .Q(Q)); endmodule + +module \$__DFFE_NP0 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("CE"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(R), .DI(D), .Q(Q)); endmodule +module \$__DFFE_NP1 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("CE"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(R), .DI(D), .Q(Q)); endmodule +module \$__DFFE_PP0 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(R), .DI(D), .Q(Q)); endmodule +module \$__DFFE_PP1 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(R), .DI(D), .Q(Q)); endmodule + +module \$__DFFSE_NN0 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("CE"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(!R), .DI(D), .Q(Q)); endmodule +module \$__DFFSE_NN1 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("CE"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(!R), .DI(D), .Q(Q)); endmodule +module \$__DFFSE_PN0 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(!R), .DI(D), .Q(Q)); endmodule +module \$__DFFSE_PN1 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(!R), .DI(D), .Q(Q)); endmodule + +module \$__DFFSE_NP0 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("CE"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(R), .DI(D), .Q(Q)); endmodule +module \$__DFFSE_NP1 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("CE"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(R), .DI(D), .Q(Q)); endmodule +module \$__DFFSE_PP0 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(R), .DI(D), .Q(Q)); endmodule +module \$__DFFSE_PP1 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(R), .DI(D), .Q(Q)); endmodule // TODO: Diamond flip-flops // module FD1P3AX(); endmodule diff --git a/techlibs/ecp5/ecp5_gsr.cc b/techlibs/ecp5/ecp5_gsr.cc new file mode 100644 index 000000000..8b8927d31 --- /dev/null +++ b/techlibs/ecp5/ecp5_gsr.cc @@ -0,0 +1,135 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2019 David Shah <david@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 + +struct Ecp5GsrPass : public Pass { + Ecp5GsrPass() : Pass("ecp5_gsr", "ECP5: handle GSR") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" ecp5_gsr [options] [selection]\n"); + log("\n"); + log("Trim active low async resets connected to GSR and resolve GSR parameter,\n"); + log("if a GSR or SGSR primitive is used in the design.\n"); + log("\n"); + log("If any cell has the GSR parameter set to \"AUTO\", this will be resolved\n"); + log("to \"ENABLED\" if a GSR primitive is present and the (* nogsr *) attribute\n"); + log("is not set, otherwise it will be resolved to \"DISABLED\".\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing ECP5_GSR pass (implement FF init values).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + // if (args[argidx] == "-singleton") { + // singleton_mode = true; + // continue; + // } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + { + log("Handling GSR in %s.\n", log_id(module)); + + SigMap sigmap(module); + + SigBit gsr; + bool found_gsr = false; + + for (auto cell : module->selected_cells()) + { + if (cell->type != ID(GSR) && cell->type != ID(SGSR)) + continue; + if (found_gsr) + log_error("Found more than one GSR or SGSR cell in module %s.\n", log_id(module)); + found_gsr = true; + SigSpec sig_gsr = cell->getPort(ID(GSR)); + if (GetSize(sig_gsr) < 1) + log_error("GSR cell %s has disconnected GSR input.\n", log_id(cell)); + gsr = sigmap(sig_gsr[0]); + } + + // Resolve GSR parameter + + for (auto cell : module->selected_cells()) + { + if (!cell->hasParam(ID(GSR)) || cell->getParam(ID(GSR)).decode_string() != "AUTO") + continue; + + bool gsren = found_gsr; + if (cell->get_bool_attribute("\\nogsr")) + gsren = false; + cell->setParam(ID(GSR), gsren ? Const("ENABLED") : Const("DISABLED")); + + } + + if (!found_gsr) + continue; + + // For finding active low FF inputs + pool<SigBit> inverted_gsr; + + log_debug("GSR net in module %s is %s.\n", log_id(module), log_signal(gsr)); + for (auto cell : module->selected_cells()) + { + if (cell->type != ID($_NOT_)) + continue; + SigSpec sig_a = cell->getPort(ID(A)), sig_y = cell->getPort(ID(Y)); + if (GetSize(sig_a) < 1 || GetSize(sig_y) < 1) + continue; + SigBit a = sigmap(sig_a[0]); + if (a == gsr) + inverted_gsr.insert(sigmap(sig_y[0])); + } + + for (auto cell : module->selected_cells()) + { + if (cell->type != ID(TRELLIS_FF)) + continue; + if (!cell->hasParam(ID(GSR)) || cell->getParam(ID(GSR)).decode_string() != "ENABLED") + continue; + if (!cell->hasParam(ID(SRMODE)) || cell->getParam(ID(SRMODE)).decode_string() != "ASYNC") + continue; + SigSpec sig_lsr = cell->getPort(ID(LSR)); + if (GetSize(sig_lsr) < 1) + continue; + SigBit lsr = sigmap(sig_lsr[0]); + if (!inverted_gsr.count(lsr)) + continue; + cell->setParam(ID(SRMODE), Const("SYNC")); + cell->unsetPort(ID(LSR)); + } + + } + } +} Ecp5GsrPass; + +PRIVATE_NAMESPACE_END diff --git a/techlibs/ecp5/synth_ecp5.cc b/techlibs/ecp5/synth_ecp5.cc index b1d3160ba..2593546e0 100644 --- a/techlibs/ecp5/synth_ecp5.cc +++ b/techlibs/ecp5/synth_ecp5.cc @@ -271,6 +271,8 @@ struct SynthEcp5Pass : public ScriptPass run("opt_expr -undriven -mux_undef"); run("simplemap"); run("ecp5_ffinit"); + run("ecp5_gsr"); + run("opt_clean"); } if (check_label("map_luts")) diff --git a/techlibs/xilinx/cells_sim.v b/techlibs/xilinx/cells_sim.v index e2ec4e525..b4657daca 100644 --- a/techlibs/xilinx/cells_sim.v +++ b/techlibs/xilinx/cells_sim.v @@ -29,24 +29,35 @@ module GND(output G); assign G = 0; endmodule -module IBUF(output O, input I); +module IBUF( + output O, + (* iopad_external_pin *) + input I); parameter IOSTANDARD = "default"; parameter IBUF_LOW_PWR = 0; assign O = I; endmodule -module OBUF(output O, input I); +module OBUF( + (* iopad_external_pin *) + output O, + input I); parameter IOSTANDARD = "default"; parameter DRIVE = 12; parameter SLEW = "SLOW"; assign O = I; endmodule -module BUFG(output O, input I); +module BUFG( + (* clkbuf_driver *) + output O, + input I); + assign O = I; endmodule module BUFGCTRL( + (* clkbuf_driver *) output O, input I0, input I1, input S0, input S1, @@ -72,7 +83,11 @@ assign O = S0_true ? I0_internal : (S1_true ? I1_internal : INIT_OUT); endmodule -module BUFHCE(output O, input I, input CE); +module BUFHCE( + (* clkbuf_driver *) + output O, + input I, + input CE); parameter [0:0] INIT_OUT = 1'b0; parameter CE_TYPE = "SYNC"; @@ -218,7 +233,9 @@ endmodule module FDRE ( (* abc_arrival=303 *) output reg Q, - input C, CE, D, R + (* clkbuf_sink *) + input C, + input CE, D, R ); parameter [0:0] INIT = 1'b0; parameter [0:0] IS_C_INVERTED = 1'b0; @@ -234,7 +251,9 @@ endmodule module FDSE ( (* abc_arrival=303 *) output reg Q, - input C, CE, D, S + (* clkbuf_sink *) + input C, + input CE, D, S ); parameter [0:0] INIT = 1'b1; parameter [0:0] IS_C_INVERTED = 1'b0; @@ -250,7 +269,9 @@ endmodule module FDCE ( (* abc_arrival=303 *) output reg Q, - input C, CE, D, CLR + (* clkbuf_sink *) + input C, + input CE, D, CLR ); parameter [0:0] INIT = 1'b0; parameter [0:0] IS_C_INVERTED = 1'b0; @@ -268,7 +289,9 @@ endmodule module FDPE ( (* abc_arrival=303 *) output reg Q, - input C, CE, D, PRE + (* clkbuf_sink *) + input C, + input CE, D, PRE ); parameter [0:0] INIT = 1'b1; parameter [0:0] IS_C_INVERTED = 1'b0; @@ -286,7 +309,9 @@ endmodule module FDRE_1 ( (* abc_arrival=303 *) output reg Q, - input C, CE, D, R + (* clkbuf_sink *) + input C, + input CE, D, R ); parameter [0:0] INIT = 1'b0; initial Q <= INIT; @@ -296,7 +321,9 @@ endmodule module FDSE_1 ( (* abc_arrival=303 *) output reg Q, - input C, CE, D, S + (* clkbuf_sink *) + input C, + input CE, D, S ); parameter [0:0] INIT = 1'b1; initial Q <= INIT; @@ -306,7 +333,9 @@ endmodule module FDCE_1 ( (* abc_arrival=303 *) output reg Q, - input C, CE, D, CLR + (* clkbuf_sink *) + input C, + input CE, D, CLR ); parameter [0:0] INIT = 1'b0; initial Q <= INIT; @@ -316,7 +345,9 @@ endmodule module FDPE_1 ( (* abc_arrival=303 *) output reg Q, - input C, CE, D, PRE + (* clkbuf_sink *) + input C, + input CE, D, PRE ); parameter [0:0] INIT = 1'b1; initial Q <= INIT; @@ -328,6 +359,7 @@ module RAM32X1D ( (* abc_arrival=1153 *) output DPO, SPO, input D, + (* clkbuf_sink *) input WCLK, input WE, input A0, A1, A2, A3, A4, @@ -349,6 +381,7 @@ module RAM64X1D ( (* abc_arrival=1153 *) output DPO, SPO, input D, + (* clkbuf_sink *) input WCLK, input WE, input A0, A1, A2, A3, A4, A5, @@ -370,6 +403,7 @@ module RAM128X1D ( (* abc_arrival=1153 *) output DPO, SPO, input D, + (* clkbuf_sink *) input WCLK, input WE, input [6:0] A, DPRA @@ -387,19 +421,45 @@ module SRL16E ( // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L904-L905 (* abc_arrival=1472 *) output Q, - input A0, A1, A2, A3, CE, CLK, D + input A0, A1, A2, A3, CE, + (* clkbuf_sink *) + input CLK, + input D +); + parameter [15:0] INIT = 16'h0000; + parameter [0:0] IS_CLK_INVERTED = 1'b0; + + reg [15:0] r = INIT; + assign Q = r[{A3,A2,A1,A0}]; + generate + if (IS_CLK_INVERTED) begin + always @(negedge CLK) if (CE) r <= { r[14:0], D }; + end + else + always @(posedge CLK) if (CE) r <= { r[14:0], D }; + endgenerate +endmodule + +module SRLC16E ( + output Q, + output Q15, + input A0, A1, A2, A3, CE, + (* clkbuf_sink *) + input CLK, + input D ); parameter [15:0] INIT = 16'h0000; parameter [0:0] IS_CLK_INVERTED = 1'b0; reg [15:0] r = INIT; + assign Q15 = r[15]; assign Q = r[{A3,A2,A1,A0}]; generate if (IS_CLK_INVERTED) begin always @(negedge CLK) if (CE) r <= { r[14:0], D }; end else - always @(posedge CLK) if (CE) r <= { r[14:0], D }; + always @(posedge CLK) if (CE) r <= { r[14:0], D }; endgenerate endmodule @@ -410,7 +470,10 @@ module SRLC32E ( (* abc_arrival=1114 *) output Q31, input [4:0] A, - input CE, CLK, D + input CE, + (* clkbuf_sink *) + input CLK, + input D ); parameter [31:0] INIT = 32'h00000000; parameter [0:0] IS_CLK_INVERTED = 1'b0; diff --git a/techlibs/xilinx/cells_xtra.py b/techlibs/xilinx/cells_xtra.py new file mode 100644 index 000000000..dd4e300ae --- /dev/null +++ b/techlibs/xilinx/cells_xtra.py @@ -0,0 +1,257 @@ +#!/usr/bin/env python3 + +from argparse import ArgumentParser +from io import StringIO +from enum import Enum, auto +import os.path +import sys + + +class Cell: + def __init__(self, name, keep=False, port_attrs={}): + self.name = name + self.keep = keep + self.port_attrs = port_attrs + + +CELLS = [ + # Design elements types listed in Xilinx UG953 + Cell('BSCANE2', keep=True), + # Cell('BUFG', port_attrs={'O': ['clkbuf_driver']}), + Cell('BUFGCE', port_attrs={'O': ['clkbuf_driver']}), + Cell('BUFGCE_1', port_attrs={'O': ['clkbuf_driver']}), + #Cell('BUFGCTRL', port_attrs={'O': ['clkbuf_driver']}), + Cell('BUFGMUX', port_attrs={'O': ['clkbuf_driver']}), + Cell('BUFGMUX_1', port_attrs={'O': ['clkbuf_driver']}), + Cell('BUFGMUX_CTRL', port_attrs={'O': ['clkbuf_driver']}), + Cell('BUFH', port_attrs={'O': ['clkbuf_driver']}), + #Cell('BUFHCE', port_attrs={'O': ['clkbuf_driver']}), + Cell('BUFIO', port_attrs={'O': ['clkbuf_driver']}), + Cell('BUFMR', port_attrs={'O': ['clkbuf_driver']}), + Cell('BUFMRCE', port_attrs={'O': ['clkbuf_driver']}), + Cell('BUFR', port_attrs={'O': ['clkbuf_driver']}), + Cell('CAPTUREE2', keep=True), + # Cell('CARRY4'), + Cell('CFGLUT5', port_attrs={'CLK': ['clkbuf_sink']}), + Cell('DCIRESET', keep=True), + Cell('DNA_PORT'), + Cell('DSP48E1', port_attrs={'CLK': ['clkbuf_sink']}), + Cell('EFUSE_USR'), + # Cell('FDCE'), + # Cell('FDPE'), + # Cell('FDRE'), + # Cell('FDSE'), + Cell('FIFO18E1', port_attrs={'RDCLK': ['clkbuf_sink'], 'WRCLK': ['clkbuf_sink']}), + Cell('FIFO36E1', port_attrs={'RDCLK': ['clkbuf_sink'], 'WRCLK': ['clkbuf_sink']}), + Cell('FRAME_ECCE2'), + Cell('GTHE2_CHANNEL'), + Cell('GTHE2_COMMON'), + Cell('GTPE2_CHANNEL'), + Cell('GTPE2_COMMON'), + Cell('GTXE2_CHANNEL'), + Cell('GTXE2_COMMON'), + # Cell('IBUF', port_attrs={'I': ['iopad_external_pin']}), + Cell('IBUF_IBUFDISABLE', port_attrs={'I': ['iopad_external_pin']}), + Cell('IBUF_INTERMDISABLE', port_attrs={'I': ['iopad_external_pin']}), + Cell('IBUFDS', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}), + Cell('IBUFDS_DIFF_OUT', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}), + Cell('IBUFDS_DIFF_OUT_IBUFDISABLE', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}), + Cell('IBUFDS_DIFF_OUT_INTERMDISABLE', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}), + Cell('IBUFDS_GTE2', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}), + Cell('IBUFDS_IBUFDISABLE', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}), + Cell('IBUFDS_INTERMDISABLE', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}), + Cell('IBUFG', port_attrs={'I': ['iopad_external_pin']}), + Cell('IBUFGDS', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}), + Cell('IBUFGDS_DIFF_OUT', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}), + Cell('ICAPE2', keep=True), + Cell('IDDR', port_attrs={'C': ['clkbuf_sink']}), + Cell('IDDR_2CLK', port_attrs={'C': ['clkbuf_sink'], 'CB': ['clkbuf_sink']}), + Cell('IDELAYCTRL', keep=True, port_attrs={'REFCLK': ['clkbuf_sink']}), + Cell('IDELAYE2', port_attrs={'C': ['clkbuf_sink']}), + Cell('IN_FIFO', port_attrs={'RDCLK': ['clkbuf_sink'], 'WRCLK': ['clkbuf_sink']}), + Cell('IOBUF', port_attrs={'IO': ['iopad_external_pin']}), + Cell('IOBUF_DCIEN', port_attrs={'IO': ['iopad_external_pin']}), + Cell('IOBUF_INTERMDISABLE', port_attrs={'IO': ['iopad_external_pin']}), + Cell('IOBUFDS', port_attrs={'IO': ['iopad_external_pin']}), + Cell('IOBUFDS_DCIEN', port_attrs={'IO': ['iopad_external_pin'], 'IOB': ['iopad_external_pin']}), + Cell('IOBUFDS_DIFF_OUT', port_attrs={'IO': ['iopad_external_pin'], 'IOB': ['iopad_external_pin']}), + Cell('IOBUFDS_DIFF_OUT_DCIEN', port_attrs={'IO': ['iopad_external_pin'], 'IOB': ['iopad_external_pin']}), + Cell('IOBUFDS_DIFF_OUT_INTERMDISABLE', port_attrs={'IO': ['iopad_external_pin'], 'IOB': ['iopad_external_pin']}), + Cell('ISERDESE2', port_attrs={ + 'CLK': ['clkbuf_sink'], + 'CLKB': ['clkbuf_sink'], + 'OCLK': ['clkbuf_sink'], + 'OCLKB': ['clkbuf_sink'], + 'CLKDIV': ['clkbuf_sink'], + 'CLKDIVP': ['clkbuf_sink'], + }), + Cell('KEEPER'), + Cell('LDCE'), + Cell('LDPE'), + # Cell('LUT1'), + # Cell('LUT2'), + # Cell('LUT3'), + # Cell('LUT4'), + # Cell('LUT5'), + # Cell('LUT6'), + #Cell('LUT6_2'), + Cell('MMCME2_ADV'), + Cell('MMCME2_BASE'), + # Cell('MUXF7'), + # Cell('MUXF8'), + # Cell('OBUF', port_attrs={'O': ['iopad_external_pin']}), + Cell('OBUFDS', port_attrs={'O': ['iopad_external_pin'], 'OB': ['iopad_external_pin']}), + Cell('OBUFT', port_attrs={'O': ['iopad_external_pin']}), + Cell('OBUFTDS', port_attrs={'O': ['iopad_external_pin'], 'OB': ['iopad_external_pin']}), + Cell('ODDR', port_attrs={'C': ['clkbuf_sink']}), + Cell('ODELAYE2', port_attrs={'C': ['clkbuf_sink']}), + Cell('OSERDESE2', port_attrs={'CLK': ['clkbuf_sink'], 'CLKDIV': ['clkbuf_sink']}), + Cell('OUT_FIFO', port_attrs={'RDCLK': ['clkbuf_sink'], 'WRCLK': ['clkbuf_sink']}), + Cell('PHASER_IN'), + Cell('PHASER_IN_PHY'), + Cell('PHASER_OUT'), + Cell('PHASER_OUT_PHY'), + Cell('PHASER_REF'), + Cell('PHY_CONTROL'), + Cell('PLLE2_ADV'), + Cell('PLLE2_BASE'), + Cell('PS7', keep=True), + Cell('PULLDOWN'), + Cell('PULLUP'), + #Cell('RAM128X1D', port_attrs={'WCLK': ['clkbuf_sink']}), + Cell('RAM128X1S', port_attrs={'WCLK': ['clkbuf_sink']}), + Cell('RAM256X1S', port_attrs={'WCLK': ['clkbuf_sink']}), + Cell('RAM32M', port_attrs={'WCLK': ['clkbuf_sink']}), + #Cell('RAM32X1D', port_attrs={'WCLK': ['clkbuf_sink']}), + Cell('RAM32X1S', port_attrs={'WCLK': ['clkbuf_sink']}), + Cell('RAM32X1S_1', port_attrs={'WCLK': ['clkbuf_sink']}), + Cell('RAM32X2S', port_attrs={'WCLK': ['clkbuf_sink']}), + Cell('RAM64M', port_attrs={'WCLK': ['clkbuf_sink']}), + #Cell('RAM64X1D', port_attrs={'WCLK': ['clkbuf_sink']}), + Cell('RAM64X1S', port_attrs={'WCLK': ['clkbuf_sink']}), + Cell('RAM64X1S_1', port_attrs={'WCLK': ['clkbuf_sink']}), + Cell('RAM64X2S', port_attrs={'WCLK': ['clkbuf_sink']}), + # Cell('RAMB18E1', port_attrs={'CLKARDCLK': ['clkbuf_sink'], 'CLKBWRCLK': ['clkbuf_sink']}), + # Cell('RAMB36E1', port_attrs={'CLKARDCLK': ['clkbuf_sink'], 'CLKBWRCLK': ['clkbuf_sink']}), + Cell('ROM128X1'), + Cell('ROM256X1'), + Cell('ROM32X1'), + Cell('ROM64X1'), + #Cell('SRL16E', port_attrs={'CLK': ['clkbuf_sink']}), + #Cell('SRLC32E', port_attrs={'CLK': ['clkbuf_sink']}), + Cell('STARTUPE2', keep=True), + Cell('USR_ACCESSE2'), + Cell('XADC'), +] + +class State(Enum): + OUTSIDE = auto() + IN_MODULE = auto() + IN_OTHER_MODULE = auto() + IN_FUNCTION = auto() + IN_TASK = auto() + +def xtract_cell_decl(cell, dirs, outf): + for dir in dirs: + fname = os.path.join(dir, cell.name + '.v') + try: + with open(fname) as f: + state = State.OUTSIDE + found = False + # Probably the most horrible Verilog "parser" ever written. + for l in f: + l = l.partition('//')[0] + l = l.strip() + if l == 'module {}'.format(cell.name) or l.startswith('module {} '.format(cell.name)): + if found: + print('Multiple modules in {}.'.format(fname)) + sys.exit(1) + elif state != State.OUTSIDE: + print('Nested modules in {}.'.format(fname)) + sys.exit(1) + found = True + state = State.IN_MODULE + if cell.keep: + outf.write('(* keep *)\n') + outf.write('module {} (...);\n'.format(cell.name)) + elif l.startswith('module '): + if state != State.OUTSIDE: + print('Nested modules in {}.'.format(fname)) + sys.exit(1) + state = State.IN_OTHER_MODULE + elif l.startswith('task '): + if state == State.IN_MODULE: + state = State.IN_TASK + elif l.startswith('function '): + if state == State.IN_MODULE: + state = State.IN_FUNCTION + elif l == 'endtask': + if state == State.IN_TASK: + state = State.IN_MODULE + elif l == 'endfunction': + if state == State.IN_FUNCTION: + state = State.IN_MODULE + elif l == 'endmodule': + if state == State.IN_MODULE: + outf.write(l + '\n') + outf.write('\n') + elif state != State.IN_OTHER_MODULE: + print('endmodule in weird place in {}.'.format(cell.name, fname)) + sys.exit(1) + state = State.OUTSIDE + elif l.startswith(('input ', 'output ', 'inout ')) and state == State.IN_MODULE: + if l.endswith((';', ',')): + l = l[:-1] + if ';' in l: + print('Weird port line in {} [{}].'.format(fname, l)) + sys.exit(1) + kind, _, ports = l.partition(' ') + for port in ports.split(','): + port = port.strip() + for attr in cell.port_attrs.get(port, []): + outf.write(' (* {} *)\n'.format(attr)) + outf.write(' {} {};\n'.format(kind, port)) + elif l.startswith('parameter ') and state == State.IN_MODULE: + if 'UNPLACED' in l: + continue + if l.endswith((';', ',')): + l = l[:-1] + while ' ' in l: + l = l.replace(' ', ' ') + if ';' in l: + print('Weird parameter line in {} [{}].'.format(fname, l)) + sys.exit(1) + outf.write(' {};\n'.format(l)) + if state != State.OUTSIDE: + print('endmodule not found in {}.'.format(fname)) + sys.exit(1) + if not found: + print('Cannot find module {} in {}.'.format(cell.name, fname)) + sys.exit(1) + return + except FileNotFoundError: + continue + print('Cannot find {}.'.format(cell.name)) + sys.exit(1) + +if __name__ == '__main__': + parser = ArgumentParser(description='Extract Xilinx blackbox cell definitions from Vivado.') + parser.add_argument('vivado_dir', nargs='?', default='/opt/Xilinx/Vivado/2018.1') + args = parser.parse_args() + + dirs = [ + os.path.join(args.vivado_dir, 'data/verilog/src/xeclib'), + os.path.join(args.vivado_dir, 'data/verilog/src/retarget'), + ] + for dir in dirs: + if not os.path.isdir(dir): + print('{} is not a directory'.format(dir)) + + out = StringIO() + for cell in CELLS: + xtract_cell_decl(cell, dirs, out) + + with open('cells_xtra.v', 'w') as f: + f.write('// Created by cells_xtra.py from Xilinx models\n') + f.write('\n') + f.write(out.getvalue()) diff --git a/techlibs/xilinx/cells_xtra.sh b/techlibs/xilinx/cells_xtra.sh deleted file mode 100644 index 53b528820..000000000 --- a/techlibs/xilinx/cells_xtra.sh +++ /dev/null @@ -1,147 +0,0 @@ -#!/bin/bash - -set -e -libdir="/opt/Xilinx/Vivado/2018.1/data/verilog/src" - -function xtract_cell_decl() -{ - for dir in $libdir/xeclib $libdir/retarget; do - [ -f $dir/$1.v ] || continue - [ -z "$2" ] || echo $2 - egrep '^\s*((end)?module|parameter|input|inout|output|(end)?function|(end)?task)' $dir/$1.v | - sed -re '/UNPLACED/ d; /^\s*function/,/endfunction/ d; /^\s*task/,/endtask/ d; - s,//.*,,; s/#?\(.*/(...);/; s/^(input|output|parameter)/ \1/; - s/\s+$//; s/,$/;/; /input|output|parameter/ s/[^;]$/&;/; s/\s+/ /g; - s/^ ((end)?module)/\1/; s/^ / /; /module.*_bb/,/endmodule/ d;' - echo; return - done - echo "Can't find $1." - exit 1 -} - -{ - echo "// Created by cells_xtra.sh from Xilinx models" - echo - - # Design elements types listed in Xilinx UG953 - xtract_cell_decl BSCANE2 - # xtract_cell_decl BUFG - xtract_cell_decl BUFGCE - xtract_cell_decl BUFGCE_1 - #xtract_cell_decl BUFGCTRL - xtract_cell_decl BUFGMUX - xtract_cell_decl BUFGMUX_1 - xtract_cell_decl BUFGMUX_CTRL - xtract_cell_decl BUFH - #xtract_cell_decl BUFHCE - xtract_cell_decl BUFIO - xtract_cell_decl BUFMR - xtract_cell_decl BUFMRCE - xtract_cell_decl BUFR - xtract_cell_decl CAPTUREE2 "(* keep *)" - # xtract_cell_decl CARRY4 - xtract_cell_decl CFGLUT5 - xtract_cell_decl DCIRESET "(* keep *)" - xtract_cell_decl DNA_PORT - xtract_cell_decl DSP48E1 - xtract_cell_decl EFUSE_USR - # xtract_cell_decl FDCE - # xtract_cell_decl FDPE - # xtract_cell_decl FDRE - # xtract_cell_decl FDSE - xtract_cell_decl FIFO18E1 - xtract_cell_decl FIFO36E1 - xtract_cell_decl FRAME_ECCE2 - xtract_cell_decl GTHE2_CHANNEL - xtract_cell_decl GTHE2_COMMON - xtract_cell_decl GTPE2_CHANNEL - xtract_cell_decl GTPE2_COMMON - xtract_cell_decl GTXE2_CHANNEL - xtract_cell_decl GTXE2_COMMON - # xtract_cell_decl IBUF - xtract_cell_decl IBUF_IBUFDISABLE - xtract_cell_decl IBUF_INTERMDISABLE - xtract_cell_decl IBUFDS - xtract_cell_decl IBUFDS_DIFF_OUT - xtract_cell_decl IBUFDS_DIFF_OUT_IBUFDISABLE - xtract_cell_decl IBUFDS_DIFF_OUT_INTERMDISABLE - xtract_cell_decl IBUFDS_GTE2 - xtract_cell_decl IBUFDS_IBUFDISABLE - xtract_cell_decl IBUFDS_INTERMDISABLE - xtract_cell_decl ICAPE2 "(* keep *)" - xtract_cell_decl IDDR - xtract_cell_decl IDDR_2CLK - xtract_cell_decl IDELAYCTRL "(* keep *)" - xtract_cell_decl IDELAYE2 - xtract_cell_decl IN_FIFO - xtract_cell_decl IOBUF - xtract_cell_decl IOBUF_DCIEN - xtract_cell_decl IOBUF_INTERMDISABLE - xtract_cell_decl IOBUFDS - xtract_cell_decl IOBUFDS_DCIEN - xtract_cell_decl IOBUFDS_DIFF_OUT - xtract_cell_decl IOBUFDS_DIFF_OUT_DCIEN - xtract_cell_decl IOBUFDS_DIFF_OUT_INTERMDISABLE - xtract_cell_decl ISERDESE2 - xtract_cell_decl KEEPER - xtract_cell_decl LDCE - xtract_cell_decl LDPE - # xtract_cell_decl LUT1 - # xtract_cell_decl LUT2 - # xtract_cell_decl LUT3 - # xtract_cell_decl LUT4 - # xtract_cell_decl LUT5 - # xtract_cell_decl LUT6 - #xtract_cell_decl LUT6_2 - xtract_cell_decl MMCME2_ADV - xtract_cell_decl MMCME2_BASE - # xtract_cell_decl MUXF7 - # xtract_cell_decl MUXF8 - # xtract_cell_decl OBUF - xtract_cell_decl OBUFDS - xtract_cell_decl OBUFT - xtract_cell_decl OBUFTDS - xtract_cell_decl ODDR - xtract_cell_decl ODELAYE2 - xtract_cell_decl OSERDESE2 - xtract_cell_decl OUT_FIFO - xtract_cell_decl PHASER_IN - xtract_cell_decl PHASER_IN_PHY - xtract_cell_decl PHASER_OUT - xtract_cell_decl PHASER_OUT_PHY - xtract_cell_decl PHASER_REF - xtract_cell_decl PHY_CONTROL - xtract_cell_decl PLLE2_ADV - xtract_cell_decl PLLE2_BASE - xtract_cell_decl PS7 "(* keep *)" - xtract_cell_decl PULLDOWN - xtract_cell_decl PULLUP - #xtract_cell_decl RAM128X1D - xtract_cell_decl RAM128X1S - xtract_cell_decl RAM256X1S - xtract_cell_decl RAM32M - #xtract_cell_decl RAM32X1D - xtract_cell_decl RAM32X1S - xtract_cell_decl RAM32X1S_1 - xtract_cell_decl RAM32X2S - xtract_cell_decl RAM64M - #xtract_cell_decl RAM64X1D - xtract_cell_decl RAM64X1S - xtract_cell_decl RAM64X1S_1 - xtract_cell_decl RAM64X2S - # xtract_cell_decl RAMB18E1 - # xtract_cell_decl RAMB36E1 - xtract_cell_decl ROM128X1 - xtract_cell_decl ROM256X1 - xtract_cell_decl ROM32X1 - xtract_cell_decl ROM64X1 - #xtract_cell_decl SRL16E - #xtract_cell_decl SRLC32E - xtract_cell_decl STARTUPE2 "(* keep *)" - xtract_cell_decl USR_ACCESSE2 - xtract_cell_decl XADC -} > cells_xtra.new - -mv cells_xtra.new cells_xtra.v -exit 0 - diff --git a/techlibs/xilinx/cells_xtra.v b/techlibs/xilinx/cells_xtra.v index 15fa1b63a..a6669b872 100644 --- a/techlibs/xilinx/cells_xtra.v +++ b/techlibs/xilinx/cells_xtra.v @@ -1,5 +1,6 @@ -// Created by cells_xtra.sh from Xilinx models +// Created by cells_xtra.py from Xilinx models +(* keep *) module BSCANE2 (...); parameter DISABLE_JTAG = "FALSE"; parameter integer JTAG_CHAIN = 1; @@ -20,29 +21,39 @@ module BUFGCE (...); parameter CE_TYPE = "SYNC"; parameter [0:0] IS_CE_INVERTED = 1'b0; parameter [0:0] IS_I_INVERTED = 1'b0; + (* clkbuf_driver *) output O; input CE; input I; endmodule module BUFGCE_1 (...); + (* clkbuf_driver *) output O; - input CE, I; + input CE; + input I; endmodule module BUFGMUX (...); parameter CLK_SEL_TYPE = "SYNC"; + (* clkbuf_driver *) output O; - input I0, I1, S; + input I0; + input I1; + input S; endmodule module BUFGMUX_1 (...); parameter CLK_SEL_TYPE = "SYNC"; + (* clkbuf_driver *) output O; - input I0, I1, S; + input I0; + input I1; + input S; endmodule module BUFGMUX_CTRL (...); + (* clkbuf_driver *) output O; input I0; input I1; @@ -50,16 +61,19 @@ module BUFGMUX_CTRL (...); endmodule module BUFH (...); + (* clkbuf_driver *) output O; input I; endmodule module BUFIO (...); + (* clkbuf_driver *) output O; input I; endmodule module BUFMR (...); + (* clkbuf_driver *) output O; input I; endmodule @@ -68,12 +82,14 @@ module BUFMRCE (...); parameter CE_TYPE = "SYNC"; parameter integer INIT_OUT = 0; parameter [0:0] IS_CE_INVERTED = 1'b0; + (* clkbuf_driver *) output O; input CE; input I; endmodule module BUFR (...); + (* clkbuf_driver *) output O; input CE; input CLR; @@ -95,8 +111,15 @@ module CFGLUT5 (...); output CDO; output O5; output O6; - input I4, I3, I2, I1, I0; - input CDI, CE, CLK; + input I4; + input I3; + input I2; + input I1; + input I0; + input CDI; + input CE; + (* clkbuf_sink *) + input CLK; endmodule (* keep *) @@ -108,7 +131,10 @@ endmodule module DNA_PORT (...); parameter [56:0] SIM_DNA_VALUE = 57'h0; output DOUT; - input CLK, DIN, READ, SHIFT; + input CLK; + input DIN; + input READ; + input SHIFT; endmodule module DSP48E1 (...); @@ -175,6 +201,7 @@ module DSP48E1 (...); input CEINMODE; input CEM; input CEP; + (* clkbuf_sink *) input CLK; input [24:0] D; input [4:0] INMODE; @@ -227,11 +254,13 @@ module FIFO18E1 (...); output WRERR; input [31:0] DI; input [3:0] DIP; + (* clkbuf_sink *) input RDCLK; input RDEN; input REGCE; input RST; input RSTREG; + (* clkbuf_sink *) input WRCLK; input WREN; endmodule @@ -272,11 +301,13 @@ module FIFO36E1 (...); input [7:0] DIP; input INJECTDBITERR; input INJECTSBITERR; + (* clkbuf_sink *) input RDCLK; input RDEN; input REGCE; input RST; input RSTREG; + (* clkbuf_sink *) input WRCLK; input WREN; endmodule @@ -1969,6 +2000,7 @@ module IBUF_IBUFDISABLE (...); parameter SIM_DEVICE = "7SERIES"; parameter USE_IBUFDISABLE = "TRUE"; output O; + (* iopad_external_pin *) input I; input IBUFDISABLE; endmodule @@ -1979,6 +2011,7 @@ module IBUF_INTERMDISABLE (...); parameter SIM_DEVICE = "7SERIES"; parameter USE_IBUFDISABLE = "TRUE"; output O; + (* iopad_external_pin *) input I; input IBUFDISABLE; input INTERMDISABLE; @@ -1993,7 +2026,10 @@ module IBUFDS (...); parameter IFD_DELAY_VALUE = "AUTO"; parameter IOSTANDARD = "DEFAULT"; output O; - input I, IB; + (* iopad_external_pin *) + input I; + (* iopad_external_pin *) + input IB; endmodule module IBUFDS_DIFF_OUT (...); @@ -2001,8 +2037,12 @@ module IBUFDS_DIFF_OUT (...); parameter DQS_BIAS = "FALSE"; parameter IBUF_LOW_PWR = "TRUE"; parameter IOSTANDARD = "DEFAULT"; - output O, OB; - input I, IB; + output O; + output OB; + (* iopad_external_pin *) + input I; + (* iopad_external_pin *) + input IB; endmodule module IBUFDS_DIFF_OUT_IBUFDISABLE (...); @@ -2014,7 +2054,9 @@ module IBUFDS_DIFF_OUT_IBUFDISABLE (...); parameter USE_IBUFDISABLE = "TRUE"; output O; output OB; + (* iopad_external_pin *) input I; + (* iopad_external_pin *) input IB; input IBUFDISABLE; endmodule @@ -2028,7 +2070,9 @@ module IBUFDS_DIFF_OUT_INTERMDISABLE (...); parameter USE_IBUFDISABLE = "TRUE"; output O; output OB; + (* iopad_external_pin *) input I; + (* iopad_external_pin *) input IB; input IBUFDISABLE; input INTERMDISABLE; @@ -2041,7 +2085,9 @@ module IBUFDS_GTE2 (...); output O; output ODIV2; input CEB; + (* iopad_external_pin *) input I; + (* iopad_external_pin *) input IB; endmodule @@ -2053,7 +2099,9 @@ module IBUFDS_IBUFDISABLE (...); parameter SIM_DEVICE = "7SERIES"; parameter USE_IBUFDISABLE = "TRUE"; output O; + (* iopad_external_pin *) input I; + (* iopad_external_pin *) input IB; input IBUFDISABLE; endmodule @@ -2066,12 +2114,50 @@ module IBUFDS_INTERMDISABLE (...); parameter SIM_DEVICE = "7SERIES"; parameter USE_IBUFDISABLE = "TRUE"; output O; + (* iopad_external_pin *) input I; + (* iopad_external_pin *) input IB; input IBUFDISABLE; input INTERMDISABLE; endmodule +module IBUFG (...); + parameter CAPACITANCE = "DONT_CARE"; + parameter IBUF_DELAY_VALUE = "0"; + parameter IBUF_LOW_PWR = "TRUE"; + parameter IOSTANDARD = "DEFAULT"; + output O; + (* iopad_external_pin *) + input I; +endmodule + +module IBUFGDS (...); + parameter CAPACITANCE = "DONT_CARE"; + parameter DIFF_TERM = "FALSE"; + parameter IBUF_DELAY_VALUE = "0"; + parameter IBUF_LOW_PWR = "TRUE"; + parameter IOSTANDARD = "DEFAULT"; + output O; + (* iopad_external_pin *) + input I; + (* iopad_external_pin *) + input IB; +endmodule + +module IBUFGDS_DIFF_OUT (...); + parameter DIFF_TERM = "FALSE"; + parameter DQS_BIAS = "FALSE"; + parameter IBUF_LOW_PWR = "TRUE"; + parameter IOSTANDARD = "DEFAULT"; + output O; + output OB; + (* iopad_external_pin *) + input I; + (* iopad_external_pin *) + input IB; +endmodule + (* keep *) module ICAPE2 (...); parameter [31:0] DEVICE_ID = 32'h04244093; @@ -2095,6 +2181,7 @@ module IDDR (...); parameter XON = "TRUE"; output Q1; output Q2; + (* clkbuf_sink *) input C; input CE; input D; @@ -2112,7 +2199,9 @@ module IDDR_2CLK (...); parameter SRTYPE = "SYNC"; output Q1; output Q2; + (* clkbuf_sink *) input C; + (* clkbuf_sink *) input CB; input CE; input D; @@ -2124,6 +2213,7 @@ endmodule module IDELAYCTRL (...); parameter SIM_DEVICE = "7SERIES"; output RDY; + (* clkbuf_sink *) input REFCLK; input RST; endmodule @@ -2143,6 +2233,7 @@ module IDELAYE2 (...); parameter integer SIM_DELAY_D = 0; output [4:0] CNTVALUEOUT; output DATAOUT; + (* clkbuf_sink *) input C; input CE; input CINVCTRL; @@ -2174,9 +2265,11 @@ module IN_FIFO (...); output [7:0] Q7; output [7:0] Q8; output [7:0] Q9; + (* clkbuf_sink *) input RDCLK; input RDEN; input RESET; + (* clkbuf_sink *) input WRCLK; input WREN; input [3:0] D0; @@ -2197,8 +2290,10 @@ module IOBUF (...); parameter IOSTANDARD = "DEFAULT"; parameter SLEW = "SLOW"; output O; + (* iopad_external_pin *) inout IO; - input I, T; + input I; + input T; endmodule module IOBUF_DCIEN (...); @@ -2209,6 +2304,7 @@ module IOBUF_DCIEN (...); parameter SLEW = "SLOW"; parameter USE_IBUFDISABLE = "TRUE"; output O; + (* iopad_external_pin *) inout IO; input DCITERMDISABLE; input I; @@ -2224,6 +2320,7 @@ module IOBUF_INTERMDISABLE (...); parameter SLEW = "SLOW"; parameter USE_IBUFDISABLE = "TRUE"; output O; + (* iopad_external_pin *) inout IO; input I; input IBUFDISABLE; @@ -2238,8 +2335,11 @@ module IOBUFDS (...); parameter IOSTANDARD = "DEFAULT"; parameter SLEW = "SLOW"; output O; - inout IO, IOB; - input I, T; + (* iopad_external_pin *) + inout IO; + inout IOB; + input I; + input T; endmodule module IOBUFDS_DCIEN (...); @@ -2251,7 +2351,9 @@ module IOBUFDS_DCIEN (...); parameter SLEW = "SLOW"; parameter USE_IBUFDISABLE = "TRUE"; output O; + (* iopad_external_pin *) inout IO; + (* iopad_external_pin *) inout IOB; input DCITERMDISABLE; input I; @@ -2266,7 +2368,9 @@ module IOBUFDS_DIFF_OUT (...); parameter IOSTANDARD = "DEFAULT"; output O; output OB; + (* iopad_external_pin *) inout IO; + (* iopad_external_pin *) inout IOB; input I; input TM; @@ -2282,7 +2386,9 @@ module IOBUFDS_DIFF_OUT_DCIEN (...); parameter USE_IBUFDISABLE = "TRUE"; output O; output OB; + (* iopad_external_pin *) inout IO; + (* iopad_external_pin *) inout IOB; input DCITERMDISABLE; input I; @@ -2300,7 +2406,9 @@ module IOBUFDS_DIFF_OUT_INTERMDISABLE (...); parameter USE_IBUFDISABLE = "TRUE"; output O; output OB; + (* iopad_external_pin *) inout IO; + (* iopad_external_pin *) inout IOB; input I; input IBUFDISABLE; @@ -2348,15 +2456,21 @@ module ISERDESE2 (...); input BITSLIP; input CE1; input CE2; + (* clkbuf_sink *) input CLK; + (* clkbuf_sink *) input CLKB; + (* clkbuf_sink *) input CLKDIV; + (* clkbuf_sink *) input CLKDIVP; input D; input DDLY; input DYNCLKDIVSEL; input DYNCLKSEL; + (* clkbuf_sink *) input OCLK; + (* clkbuf_sink *) input OCLKB; input OFB; input RST; @@ -2375,7 +2489,10 @@ module LDCE (...); parameter MSGON = "TRUE"; parameter XON = "TRUE"; output Q; - input CLR, D, G, GE; + input CLR; + input D; + input G; + input GE; endmodule module LDPE (...); @@ -2385,7 +2502,10 @@ module LDPE (...); parameter MSGON = "TRUE"; parameter XON = "TRUE"; output Q; - input D, G, GE, PRE; + input D; + input G; + input GE; + input PRE; endmodule module MMCME2_ADV (...); @@ -2533,7 +2653,10 @@ module OBUFDS (...); parameter CAPACITANCE = "DONT_CARE"; parameter IOSTANDARD = "DEFAULT"; parameter SLEW = "SLOW"; - output O, OB; + (* iopad_external_pin *) + output O; + (* iopad_external_pin *) + output OB; input I; endmodule @@ -2542,20 +2665,27 @@ module OBUFT (...); parameter integer DRIVE = 12; parameter IOSTANDARD = "DEFAULT"; parameter SLEW = "SLOW"; + (* iopad_external_pin *) output O; - input I, T; + input I; + input T; endmodule module OBUFTDS (...); parameter CAPACITANCE = "DONT_CARE"; parameter IOSTANDARD = "DEFAULT"; parameter SLEW = "SLOW"; - output O, OB; - input I, T; + (* iopad_external_pin *) + output O; + (* iopad_external_pin *) + output OB; + input I; + input T; endmodule module ODDR (...); output Q; + (* clkbuf_sink *) input C; input CE; input D1; @@ -2586,6 +2716,7 @@ module ODELAYE2 (...); parameter integer SIM_DELAY_D = 0; output [4:0] CNTVALUEOUT; output DATAOUT; + (* clkbuf_sink *) input C; input CE; input CINVCTRL; @@ -2631,7 +2762,9 @@ module OSERDESE2 (...); output TBYTEOUT; output TFB; output TQ; + (* clkbuf_sink *) input CLK; + (* clkbuf_sink *) input CLKDIV; input D1; input D2; @@ -2673,9 +2806,11 @@ module OUT_FIFO (...); output [3:0] Q9; output [7:0] Q5; output [7:0] Q6; + (* clkbuf_sink *) input RDCLK; input RDEN; input RESET; + (* clkbuf_sink *) input WRCLK; input WREN; input [7:0] D0; @@ -3659,7 +3794,17 @@ module RAM128X1S (...); parameter [127:0] INIT = 128'h00000000000000000000000000000000; parameter [0:0] IS_WCLK_INVERTED = 1'b0; output O; - input A0, A1, A2, A3, A4, A5, A6, D, WCLK, WE; + input A0; + input A1; + input A2; + input A3; + input A4; + input A5; + input A6; + input D; + (* clkbuf_sink *) + input WCLK; + input WE; endmodule module RAM256X1S (...); @@ -3668,6 +3813,7 @@ module RAM256X1S (...); output O; input [7:0] A; input D; + (* clkbuf_sink *) input WCLK; input WE; endmodule @@ -3690,6 +3836,7 @@ module RAM32M (...); input [1:0] DIB; input [1:0] DIC; input [1:0] DID; + (* clkbuf_sink *) input WCLK; input WE; endmodule @@ -3698,22 +3845,48 @@ module RAM32X1S (...); parameter [31:0] INIT = 32'h00000000; parameter [0:0] IS_WCLK_INVERTED = 1'b0; output O; - input A0, A1, A2, A3, A4, D, WCLK, WE; + input A0; + input A1; + input A2; + input A3; + input A4; + input D; + (* clkbuf_sink *) + input WCLK; + input WE; endmodule module RAM32X1S_1 (...); parameter [31:0] INIT = 32'h00000000; parameter [0:0] IS_WCLK_INVERTED = 1'b0; output O; - input A0, A1, A2, A3, A4, D, WCLK, WE; + input A0; + input A1; + input A2; + input A3; + input A4; + input D; + (* clkbuf_sink *) + input WCLK; + input WE; endmodule module RAM32X2S (...); parameter [31:0] INIT_00 = 32'h00000000; parameter [31:0] INIT_01 = 32'h00000000; parameter [0:0] IS_WCLK_INVERTED = 1'b0; - output O0, O1; - input A0, A1, A2, A3, A4, D0, D1, WCLK, WE; + output O0; + output O1; + input A0; + input A1; + input A2; + input A3; + input A4; + input D0; + input D1; + (* clkbuf_sink *) + input WCLK; + input WE; endmodule module RAM64M (...); @@ -3734,6 +3907,7 @@ module RAM64M (...); input DIB; input DIC; input DID; + (* clkbuf_sink *) input WCLK; input WE; endmodule @@ -3742,46 +3916,97 @@ module RAM64X1S (...); parameter [63:0] INIT = 64'h0000000000000000; parameter [0:0] IS_WCLK_INVERTED = 1'b0; output O; - input A0, A1, A2, A3, A4, A5, D, WCLK, WE; + input A0; + input A1; + input A2; + input A3; + input A4; + input A5; + input D; + (* clkbuf_sink *) + input WCLK; + input WE; endmodule module RAM64X1S_1 (...); parameter [63:0] INIT = 64'h0000000000000000; parameter [0:0] IS_WCLK_INVERTED = 1'b0; output O; - input A0, A1, A2, A3, A4, A5, D, WCLK, WE; + input A0; + input A1; + input A2; + input A3; + input A4; + input A5; + input D; + (* clkbuf_sink *) + input WCLK; + input WE; endmodule module RAM64X2S (...); parameter [63:0] INIT_00 = 64'h0000000000000000; parameter [63:0] INIT_01 = 64'h0000000000000000; parameter [0:0] IS_WCLK_INVERTED = 1'b0; - output O0, O1; - input A0, A1, A2, A3, A4, A5, D0, D1, WCLK, WE; + output O0; + output O1; + input A0; + input A1; + input A2; + input A3; + input A4; + input A5; + input D0; + input D1; + (* clkbuf_sink *) + input WCLK; + input WE; endmodule module ROM128X1 (...); parameter [127:0] INIT = 128'h00000000000000000000000000000000; output O; - input A0, A1, A2, A3, A4, A5, A6; + input A0; + input A1; + input A2; + input A3; + input A4; + input A5; + input A6; endmodule module ROM256X1 (...); parameter [255:0] INIT = 256'h0000000000000000000000000000000000000000000000000000000000000000; output O; - input A0, A1, A2, A3, A4, A5, A6, A7; + input A0; + input A1; + input A2; + input A3; + input A4; + input A5; + input A6; + input A7; endmodule module ROM32X1 (...); parameter [31:0] INIT = 32'h00000000; output O; - input A0, A1, A2, A3, A4; + input A0; + input A1; + input A2; + input A3; + input A4; endmodule module ROM64X1 (...); parameter [63:0] INIT = 64'h0000000000000000; output O; - input A0, A1, A2, A3, A4, A5; + input A0; + input A1; + input A2; + input A3; + input A4; + input A5; endmodule (* keep *) diff --git a/techlibs/xilinx/synth_xilinx.cc b/techlibs/xilinx/synth_xilinx.cc index 8d0cf84f3..db8c9fa31 100644 --- a/techlibs/xilinx/synth_xilinx.cc +++ b/techlibs/xilinx/synth_xilinx.cc @@ -63,6 +63,9 @@ struct SynthXilinxPass : public ScriptPass log(" generate an output netlist (and BLIF file) suitable for VPR\n"); log(" (this feature is experimental and incomplete)\n"); log("\n"); + log(" -ise\n"); + log(" generate an output netlist suitable for ISE (enables -iopad)\n"); + log("\n"); log(" -nobram\n"); log(" do not use block RAM cells in output netlist\n"); log("\n"); @@ -78,6 +81,15 @@ struct SynthXilinxPass : public ScriptPass log(" -nowidelut\n"); log(" do not use MUXF[78] resources to implement LUTs larger than LUT6s\n"); log("\n"); + log(" -iopad\n"); + log(" enable I/O buffer insertion (selected automatically by -ise)\n"); + log("\n"); + log(" -noiopad\n"); + log(" disable I/O buffer insertion (only useful with -ise)\n"); + log("\n"); + log(" -noclkbuf\n"); + log(" disable automatic clock buffer insertion\n"); + log("\n"); log(" -widemux <int>\n"); log(" enable inference of hard multiplexer resources (MUXF[78]) for muxes at or\n"); log(" above this number of inputs (minimum value 2, recommended value >= 5).\n"); @@ -104,7 +116,8 @@ struct SynthXilinxPass : public ScriptPass } std::string top_opt, edif_file, blif_file, family; - bool flatten, retime, vpr, nobram, nolutram, nosrl, nocarry, nowidelut, abc9; + bool flatten, retime, vpr, ise, iopad, noiopad, noclkbuf, nobram, nolutram, nosrl, nocarry, nowidelut, abc9; + bool flatten_before_abc; int widemux; void clear_flags() YS_OVERRIDE @@ -116,6 +129,10 @@ struct SynthXilinxPass : public ScriptPass flatten = false; retime = false; vpr = false; + ise = false; + iopad = false; + noiopad = false; + noclkbuf = false; nocarry = false; nobram = false; nolutram = false; @@ -123,6 +140,7 @@ struct SynthXilinxPass : public ScriptPass nocarry = false; nowidelut = false; abc9 = false; + flatten_before_abc = false; widemux = 0; } @@ -162,6 +180,10 @@ struct SynthXilinxPass : public ScriptPass flatten = true; continue; } + if (args[argidx] == "-flatten_before_abc") { + flatten_before_abc = true; + continue; + } if (args[argidx] == "-retime") { retime = true; continue; @@ -178,6 +200,22 @@ struct SynthXilinxPass : public ScriptPass vpr = true; continue; } + if (args[argidx] == "-ise") { + ise = true; + continue; + } + if (args[argidx] == "-iopad") { + iopad = true; + continue; + } + if (args[argidx] == "-noiopad") { + noiopad = true; + continue; + } + if (args[argidx] == "-noclkbuf") { + noclkbuf = true; + continue; + } if (args[argidx] == "-nocarry") { nocarry = true; continue; @@ -385,6 +423,8 @@ struct SynthXilinxPass : public ScriptPass if (check_label("map_luts")) { run("opt_expr -mux_undef"); + if (flatten_before_abc) + run("flatten"); if (help_mode) run("abc -luts 2:2,3,6:5[,10,20] [-dff]", "(option for 'nowidelut'; option for '-retime')"); else if (abc9) { @@ -418,6 +458,18 @@ struct SynthXilinxPass : public ScriptPass run("clean"); } + if (check_label("finalize")) { + bool do_iopad = iopad || (ise && !noiopad); + if (help_mode || !noclkbuf) { + if (help_mode || do_iopad) + run("clkbufmap -buf BUFG O:I -inpad IBUFG O:I", "(skip if '-noclkbuf', '-inpad' passed if '-iopad' or '-ise' and not '-noiopad')"); + else + run("clkbufmap -buf BUFG O:I"); + } + if (do_iopad) + run("iopadmap -bits -outpad OBUF I:O -inpad IBUF O:I A:top", "(only if '-iopad' or '-ise' and not '-noiopad')"); + } + if (check_label("check")) { run("hierarchy -check"); run("stat -tech xilinx"); diff --git a/techlibs/xilinx/xc6s_brams_bb.v b/techlibs/xilinx/xc6s_brams_bb.v index eb1a29579..041d6b54f 100644 --- a/techlibs/xilinx/xc6s_brams_bb.v +++ b/techlibs/xilinx/xc6s_brams_bb.v @@ -1,5 +1,7 @@ module RAMB8BWER ( + (* clkbuf_sink *) input CLKAWRCLK, + (* clkbuf_sink *) input CLKBRDCLK, input ENAWREN, input ENBRDEN, @@ -87,7 +89,9 @@ module RAMB8BWER ( endmodule module RAMB16BWER ( + (* clkbuf_sink *) input CLKA, + (* clkbuf_sink *) input CLKB, input ENA, input ENB, diff --git a/techlibs/xilinx/xc7_brams_bb.v b/techlibs/xilinx/xc7_brams_bb.v index 56939089c..5b40a457d 100644 --- a/techlibs/xilinx/xc7_brams_bb.v +++ b/techlibs/xilinx/xc7_brams_bb.v @@ -1,7 +1,9 @@ // Max delays from https://github.com/SymbiFlow/prjxray-db/blob/f8e0364116b2983ac72a3dc8c509ea1cc79e2e3d/artix7/timings/BRAM_L.sdf#L138-L147 module RAMB18E1 ( + (* clkbuf_sink *) input CLKARDCLK, + (* clkbuf_sink *) input CLKBWRCLK, input ENARDEN, input ENBWREN, @@ -129,7 +131,9 @@ module RAMB18E1 ( endmodule module RAMB36E1 ( + (* clkbuf_sink *) input CLKARDCLK, + (* clkbuf_sink *) input CLKBWRCLK, input ENARDEN, input ENBWREN, diff --git a/tests/sat/initval.v b/tests/sat/initval.v index 5b661f8d6..81f71b5ba 100644 --- a/tests/sat/initval.v +++ b/tests/sat/initval.v @@ -1,6 +1,7 @@ -module test(input clk, input [3:0] bar, output [3:0] foo); +module test(input clk, input [3:0] bar, output [3:0] foo, asdf); reg [3:0] foo = 0; reg [3:0] last_bar = 0; + reg [3:0] asdf = 4'b1xxx; always @* foo[1:0] <= bar[1:0]; @@ -11,5 +12,10 @@ module test(input clk, input [3:0] bar, output [3:0] foo); always @(posedge clk) last_bar <= bar; + always @(posedge clk) + asdf[3] <= bar[3]; + always @* + asdf[2:0] = 3'b111; + assert property (foo == {last_bar[3:2], bar[1:0]}); endmodule diff --git a/tests/techmap/.gitignore b/tests/techmap/.gitignore index 397b4a762..cfed22fc5 100644 --- a/tests/techmap/.gitignore +++ b/tests/techmap/.gitignore @@ -1 +1,2 @@ *.log +/*.mk diff --git a/tests/techmap/clkbufmap.ys b/tests/techmap/clkbufmap.ys new file mode 100644 index 000000000..f1277864e --- /dev/null +++ b/tests/techmap/clkbufmap.ys @@ -0,0 +1,96 @@ +read_verilog <<EOT +module clkbuf (input i, (* clkbuf_driver *) output o); endmodule +module dff ((* clkbuf_sink *) input clk, input d, output q); endmodule +module dffe ((* clkbuf_sink *) input c, input d, e, output q); endmodule +module latch (input e, d, output q); endmodule +module clkgen (output o); endmodule + +module top(input clk1, clk2, clk3, d, e, output [4:0] q); +wire clk4, clk5, clk6; +dff s0 (.clk(clk1), .d(d), .q(q[0])); +dffe s1 (.c(clk2), .d(d), .e(e), .q(q[1])); +latch s2 (.e(clk3), .d(d), .q(q[2])); +sub s3 (.sclk4(clk4), .sclk5(clk5), .sclk6(clk6), .sd(d), .sq(q[3])); +dff s4 (.clk(clk4), .d(d), .q(q[4])); +dff s5 (.clk(clk5), .d(d), .q(q[4])); +dff s6 (.clk(clk6), .d(d), .q(q[4])); +endmodule + +module sub(output sclk4, output sclk5, output sclk6, input sd, output sq); +wire tmp; +clkgen s7(.o(sclk4)); +clkgen s8(.o(sclk5)); +clkgen s9(.o(tmp)); +clkbuf s10(.i(tmp), .o(sclk6)); +dff s11(.clk(sclk4), .d(sd), .q(sq)); +endmodule +EOT + +hierarchy -auto-top +design -save ref + +# ---------------------- + +design -load ref +clkbufmap -buf clkbuf o:i +select -assert-count 3 top/t:clkbuf +select -assert-count 2 sub/t:clkbuf +select -set clk1 w:clk1 %a %co t:clkbuf %i # Find 'clk1' fanouts that are 'clkbuf' +select -assert-count 1 @clk1 # Check there is one such fanout +select -assert-count 1 @clk1 %x:+[o] %co c:s* %i # Check that the 'o' of that clkbuf drives one fanout +select -assert-count 1 @clk1 %x:+[o] %co c:s0 %i # And that one fanout is 's0' +select -set clk2 w:clk2 %a %co t:clkbuf %i +select -assert-count 1 @clk2 +select -assert-count 1 @clk2 %x:+[o] %co c:s* %i +select -assert-count 1 @clk2 %x:+[o] %co c:s1 %i +select -set clk5 w:clk5 %a %ci t:clkbuf %i +select -assert-count 1 @clk5 +select -assert-count 1 @clk5 %x:+[o] %co c:s5 %i +select -assert-count 1 @clk5 %x:+[i] %ci c:s3 %i +select -set sclk4 w:sclk4 %a %ci t:clkbuf %i +select -assert-count 1 @sclk4 +select -assert-count 1 @sclk4 %x:+[o] %co c:s11 %i +select -assert-count 1 @sclk4 %x:+[i] %ci c:s7 %i + +# ---------------------- + +design -load ref +setattr -set clkbuf_inhibit 0 w:clk1 +setattr -set clkbuf_inhibit 1 w:clk2 +clkbufmap -buf clkbuf o:i +select -assert-count 2 top/t:clkbuf +select -set clk1 w:clk1 %a %co t:clkbuf %i # Find 'clk1' fanouts that are 'clkbuf' +select -assert-count 1 @clk1 # Check there is one such fanout +select -assert-count 1 @clk1 %x:+[o] %co c:s* %i # Check that the 'o' of that clkbuf drives one fanout +select -assert-count 1 @clk1 %x:+[o] %co c:s0 %i # And that one fanout is 's0' +select -assert-count 0 w:clk2 %a %co t:clkbuf %i + +# ---------------------- + +design -load ref +setattr -set clkbuf_inhibit 1 w:clk1 +setattr -set buffer_type "bufg" w:clk2 +clkbufmap -buf clkbuf o:i w:* a:buffer_type=none a:buffer_type=bufr %u %d +select -assert-count 3 top/t:clkbuf +select -assert-count 2 sub/t:clkbuf +select -set clk1 w:clk1 %a %co t:clkbuf %i # Find 'clk1' fanouts that are 'clkbuf' +select -assert-count 1 @clk1 # Check there is one such fanout +select -assert-count 1 @clk1 %x:+[o] %co c:s* %i # Check that the 'o' of that clkbuf drives one fanout +select -assert-count 1 @clk1 %x:+[o] %co c:s0 %i # And that one fanout is 's0' +select -set clk2 w:clk2 %a %co t:clkbuf %i # Find 'clk1' fanouts that are 'clkbuf' +select -assert-count 1 @clk2 # Check there is one such fanout +select -assert-count 1 @clk2 %x:+[o] %co c:s* %i # Check that the 'o' of that clkbuf drives one fanout +select -assert-count 1 @clk2 %x:+[o] %co c:s1 %i # And that one fanout is 's0' + +# ---------------------- + +design -load ref +setattr -set buffer_type "none" w:clk1 +setattr -set buffer_type "bufr" w:clk2 +setattr -set buffer_type "bufr" w:sclk4 +setattr -set buffer_type "bufr" w:sclk5 +clkbufmap -buf clkbuf o:i w:* a:buffer_type=none a:buffer_type=bufr %u %d +select -assert-count 0 w:clk1 %a %co t:clkbuf %i +select -assert-count 0 w:clk2 %a %co t:clkbuf %i +select -assert-count 0 top/t:clkbuf +select -assert-count 1 sub/t:clkbuf diff --git a/tests/techmap/run-test.sh b/tests/techmap/run-test.sh index e2fc11e52..96489ff15 100755 --- a/tests/techmap/run-test.sh +++ b/tests/techmap/run-test.sh @@ -1,10 +1,20 @@ -#!/bin/bash +#!/usr/bin/env bash set -e -for x in *_runtest.sh; do - echo "Running $x.." - if ! bash $x &> ${x%.sh}.log; then - tail ${x%.sh}.log - echo ERROR - exit 1 +{ +echo "all::" +for x in *.ys; do + 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 "all:: run-$s" + echo "run-$s:" + echo " @echo 'Running $s..'" + echo " @bash $s > ${s%.sh}.log 2>&1" fi done +} > run-test.mk +exec ${MAKE:-make} -f run-test.mk |