3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2025-06-05 21:53:24 +00:00

Merge branch 'master' into mwk/xilinx_bufgmap

This commit is contained in:
Eddie Hung 2019-08-26 13:25:17 -07:00
commit a098205479
8 changed files with 405 additions and 60 deletions

118
CHANGELOG
View file

@ -12,7 +12,10 @@ Yosys 0.9 .. Yosys 0.9-dev
- Added "synth_xilinx -abc9" (experimental) - Added "synth_xilinx -abc9" (experimental)
- Added "synth_ice40 -abc9" (experimental) - Added "synth_ice40 -abc9" (experimental)
- Added "synth -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) - "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_ice40 (e.g. dram -> map_lutram; -nodram -> -nolutram)
- Renamed labels/options in synth_ecp5 (e.g. dram -> map_lutram; -nodram -> -nolutram) - Renamed labels/options in synth_ecp5 (e.g. dram -> map_lutram; -nodram -> -nolutram)
@ -32,33 +35,126 @@ Yosys 0.9 .. Yosys 0.9-dev
- Added "opt_share" pass, run as part of "opt -full" - Added "opt_share" pass, run as part of "opt -full"
- Added "ice40_wrapcarry" to encapsulate SB_LUT+SB_CARRY pairs for techmapping - Added "ice40_wrapcarry" to encapsulate SB_LUT+SB_CARRY pairs for techmapping
- Removed "ice40_unlut" - Removed "ice40_unlut"
- Improvements in pmgen: slices, choices, define, generate
Yosys 0.8 .. Yosys 0.8-dev Yosys 0.8 .. Yosys 0.9
-------------------------- ----------------------
* Various * Various
- Added $changed support to read_verilog - Many bugfixes and small improvements
- Added support for SystemVerilog interfaces and modports
- Added "write_edif -attrprop" - Added "write_edif -attrprop"
- Added "ice40_unlut" pass
- Added "opt_lut" pass - Added "opt_lut" pass
- Added "synth_ice40 -relut"
- Added "synth_ice40 -noabc"
- Added "gate2lut.v" techmap rule - Added "gate2lut.v" techmap rule
- Added "rename -src" - Added "rename -src"
- Added "equiv_opt" pass - 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 "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 -mux{4,8,16}=<cost>"
- Added "muxcover -dmux=<cost>" - Added "muxcover -dmux=<cost>"
- Added "muxcover -nopartial" - Added "muxcover -nopartial"
- Added "muxpack" pass - Added "muxpack" pass
- Added "pmux2shiftx -norange" - 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 -nocarry"
- Added "synth_xilinx -nowidelut" - Added "synth_xilinx -nowidelut"
- Added "synth_ecp5 -nowidelut"
- "synth_xilinx" to now infer hard shift registers (-nosrl to disable) - "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 Yosys 0.7 .. Yosys 0.8
---------------------- ----------------------

View file

@ -115,7 +115,7 @@ LDFLAGS += -rdynamic
LDLIBS += -lrt LDLIBS += -lrt
endif 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) GIT_REV := $(shell cd $(YOSYS_SRC) && git rev-parse --short HEAD 2> /dev/null || echo UNKNOWN)
OBJS = kernel/version_$(GIT_REV).o OBJS = kernel/version_$(GIT_REV).o

View file

@ -508,23 +508,17 @@ class TupleTranslator(PythonDictTranslator):
#Generate c++ code to translate to a boost::python::tuple #Generate c++ code to translate to a boost::python::tuple
@classmethod @classmethod
def translate_cpp(c, varname, types, prefix, ref): def translate_cpp(c, varname, types, prefix, ref):
text = prefix + TupleTranslator.typename + " " + varname + "___tmp = boost::python::make_tuple(" + varname + ".first, " + varname + ".second);" # if the tuple is a pair of SigSpecs (aka SigSig), then we need
return text # to call get_py_obj() on each item in the tuple
tmp_name = "tmp_" + str(Translator.tmp_cntr) if types[0].name in classnames:
Translator.tmp_cntr = Translator.tmp_cntr + 1 first_var = types[0].name + "::get_py_obj(" + varname + ".first)"
if ref:
text += prefix + "for(auto " + tmp_name + " : *" + varname + ")"
else: else:
text += prefix + "for(auto " + tmp_name + " : " + varname + ")" first_var = varname + ".first"
text += prefix + "{" if types[1].name in classnames:
if types[0].name.split(" ")[-1] in primitive_types or types[0].name in enum_names: second_var = types[1].name + "::get_py_obj(" + varname + ".second)"
text += prefix + "\t" + varname + "___tmp.append(" + tmp_name + ");" else:
elif types[0].name in known_containers: second_var = varname + ".second"
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 + TupleTranslator.typename + " " + varname + "___tmp = boost::python::make_tuple(" + first_var + ", " + second_var + ");"
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 + "}"
return text return text
#Associate the Translators with their c++ type #Associate the Translators with their c++ type

View file

@ -178,6 +178,45 @@ evaluates to `false`.
The `semioptional` statement marks matches that must match if at least one 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`. 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 Additional code
--------------- ---------------
@ -326,7 +365,7 @@ test-case generation. For example:
match mul match mul
... ...
generate 10 generate 10 0
SigSpec Y = port(ff, \D); SigSpec Y = port(ff, \D);
SigSpec A = module->addWire(NEW_ID, GetSize(Y) - rng(GetSize(Y)/2)); SigSpec A = module->addWire(NEW_ID, GetSize(Y) - rng(GetSize(Y)/2));
SigSpec B = 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 expression `rng(n)` returns a non-negative integer less than `n`.
The argument to `generate` is the chance of this generate block being executed The first argument to `generate` is the chance of this generate block being
when the match block did not match anything, in percent. 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 special statement `finish` can be used within generate blocks to terminate
the current pattern matcher run. the current pattern matcher run.

View file

@ -207,9 +207,10 @@ def process_pmgfile(f, filename):
state_types[current_pattern][line[1]] = "Cell*"; state_types[current_pattern][line[1]] = "Cell*";
block["if"] = list() block["if"] = list()
block["select"] = list() block["setup"] = list()
block["index"] = list() block["index"] = list()
block["filter"] = list() block["filter"] = list()
block["sets"] = list()
block["optional"] = False block["optional"] = False
block["semioptional"] = False block["semioptional"] = False
@ -228,7 +229,22 @@ def process_pmgfile(f, filename):
if a[0] == "select": if a[0] == "select":
b = l.lstrip()[6:] 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 continue
if a[0] == "index": if a[0] == "index":
@ -242,6 +258,11 @@ def process_pmgfile(f, filename):
block["filter"].append(rewrite_cpp(b.strip())) block["filter"].append(rewrite_cpp(b.strip()))
continue 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": if a[0] == "optional":
block["optional"] = True block["optional"] = True
continue continue
@ -252,14 +273,16 @@ def process_pmgfile(f, filename):
if a[0] == "generate": if a[0] == "generate":
block["genargs"] = list([int(s) for s in a[1:]]) 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() block["gencode"] = list()
assert len(block["genargs"]) < 2
while True: while True:
linenr += 1 linenr += 1
l = f.readline() l = f.readline()
assert l != "" assert l != ""
a = l.split() a = l.split()
if a[0] == "endmatch": break if len(a) == 1 and a[0] == "endmatch": break
block["gencode"].append(rewrite_cpp(l.rstrip())) block["gencode"].append(rewrite_cpp(l.rstrip()))
break break
@ -357,8 +380,17 @@ with open(outfile, "w") as f:
index_types = list() index_types = list()
for entry in block["index"]: for entry in block["index"]:
index_types.append(entry[0]) 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(" 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(" dict<SigBit, pool<Cell*>> sigusers;", file=f)
print(" pool<Cell*> blacklist_cells;", file=f) print(" pool<Cell*> blacklist_cells;", file=f)
print(" pool<Cell*> autoremove_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(" void add_siguser(const SigSpec &sig, Cell *cell) {", file=f)
print(" for (auto bit : sigmap(sig)) {", file=f) print(" for (auto bit : sigmap(sig)) {", file=f)
print(" if (bit.wire == nullptr) continue;", 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(" sigusers[bit].insert(cell);", file=f)
print(" }", file=f) print(" }", file=f)
print(" }", file=f) print(" }", file=f)
@ -446,10 +476,11 @@ with open(outfile, "w") as f:
else: else:
print(" ud_{}.{} = {}();".format(current_pattern, s, t), file=f) print(" ud_{}.{} = {}();".format(current_pattern, s, t), file=f)
current_pattern = None 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(" for (auto &conn : cell->connections())", file=f)
print(" add_siguser(conn.second, cell);", file=f) print(" add_siguser(conn.second, cell);", file=f)
print(" }", file=f)
print(" for (auto cell : cells) {", file=f) print(" for (auto cell : cells) {", file=f)
for index in range(len(blocks)): for index in range(len(blocks)):
@ -457,12 +488,34 @@ with open(outfile, "w") as f:
if block["type"] == "match": if block["type"] == "match":
print(" do {", file=f) print(" do {", file=f)
print(" Cell *{} = cell;".format(block["cell"]), file=f) print(" Cell *{} = cell;".format(block["cell"]), file=f)
for expr in block["select"]: print(" index_{}_value_type value;".format(index), file=f)
print(" if (!({})) break;".format(expr), 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) print(" index_{}_key_type key;".format(index), file=f)
for field, entry in enumerate(block["index"]): for field, entry in enumerate(block["index"]):
print(" std::get<{}>(key) = {};".format(field, entry[1]), file=f) 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(" } while (0);", file=f)
print(" }", file=f) print(" }", file=f)
@ -535,6 +588,8 @@ with open(outfile, "w") as f:
const_st.add(s) const_st.add(s)
elif blocks[i]["type"] == "match": elif blocks[i]["type"] == "match":
const_st.add(blocks[i]["cell"]) const_st.add(blocks[i]["cell"])
for item in blocks[i]["sets"]:
const_st.add(item[0])
else: else:
assert False assert False
@ -548,6 +603,10 @@ with open(outfile, "w") as f:
s = block["cell"] s = block["cell"]
assert s not in const_st assert s not in const_st
nonconst_st.add(s) 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: else:
assert False assert False
@ -570,7 +629,7 @@ with open(outfile, "w") as f:
print("", file=f) print("", file=f)
for s in sorted(restore_st): for s in sorted(restore_st):
t = state_types[current_pattern][s] 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": if block["type"] == "code":
print("", file=f) print("", file=f)
@ -610,7 +669,7 @@ with open(outfile, "w") as f:
print("", file=f) print("", file=f)
for s in sorted(restore_st): for s in sorted(restore_st):
t = state_types[current_pattern][s] 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): for s in sorted(nonconst_st):
if s not in restore_st: if s not in restore_st:
t = state_types[current_pattern][s] t = state_types[current_pattern][s]
@ -622,7 +681,7 @@ with open(outfile, "w") as f:
elif block["type"] == "match": elif block["type"] == "match":
assert len(restore_st) == 0 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"]): if len(block["if"]):
for expr in block["if"]: for expr in block["if"]:
@ -630,7 +689,7 @@ with open(outfile, "w") as f:
print(" if (!({})) {{".format(expr), file=f) print(" if (!({})) {{".format(expr), file=f)
print(" {} = nullptr;".format(block["cell"]), file=f) print(" {} = nullptr;".format(block["cell"]), file=f)
print(" block_{}(recursion+1);".format(index+1), 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(" return;", file=f)
print(" }", file=f) print(" }", file=f)
@ -645,21 +704,37 @@ with open(outfile, "w") as f:
print("", file=f) print("", file=f)
print(" if (cells_ptr != index_{}.end()) {{".format(index), 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(" const vector<index_{}_value_type> &cells = cells_ptr->second;".format(index), file=f)
print(" for (int idx = 0; idx < GetSize(cells); idx++) {", file=f) print(" for (int _pmg_idx = 0; _pmg_idx < GetSize(cells); _pmg_idx++) {", file=f)
print(" {} = cells[idx];".format(block["cell"]), 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) print(" if (blacklist_cells.count({})) continue;".format(block["cell"]), file=f)
for expr in block["filter"]: for expr in block["filter"]:
print(" if (!({})) continue;".format(expr), file=f) print(" if (!({})) continue;".format(expr), file=f)
if block["semioptional"] or block["genargs"] is not None: if block["semioptional"] or block["genargs"] is not None:
print(" found_any_match = true;", file=f) 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) 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(" if (rollback_ptr.second)", file=f)
print(" rollback_cache.erase(rollback_ptr.first);", file=f) print(" rollback_cache.erase(rollback_ptr.first);", file=f)
print(" if (rollback) {", file=f) print(" if (rollback) {", file=f)
print(" if (rollback != recursion) {{".format(index+1), 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(" return;", file=f)
print(" }", file=f) print(" }", file=f)
print(" rollback = 0;", file=f) print(" rollback = 0;", file=f)
@ -676,13 +751,11 @@ with open(outfile, "w") as f:
if block["semioptional"]: if block["semioptional"]:
print(" if (!found_any_match) block_{}(recursion+1);".format(index+1), file=f) 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: if block["genargs"] is not None:
print("#define finish do { rollback = -1; return; } while(0)", file=f) print("#define finish do { rollback = -1; return; } while(0)", file=f)
print(" if (generate_mode && !found_any_match) {", file=f) print(" if (generate_mode && rng(100) < (found_any_match ? {} : {})) {{".format(block["genargs"][1], block["genargs"][0]), file=f)
if len(block["genargs"]) == 1:
print(" if (rng(100) >= {}) return;".format(block["genargs"][0]), file=f)
for line in block["gencode"]: for line in block["gencode"]:
print(" " + line, file=f) print(" " + line, file=f)
print(" }", file=f) print(" }", file=f)

View file

@ -99,6 +99,24 @@ void reduce_tree(test_pmgen_pm &pm)
log(" -> %s (%s)\n", log_id(c), log_id(c->type)); 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) \ #define GENERATE_PATTERN(pmclass, pattern) \
generate_pattern<pmclass>([](pmclass &pm, std::function<void()> f){ return pm.run_ ## pattern(f); }, #pmclass, #pattern, design) 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); log("Generating \"%s\" patterns for pattern matcher \"%s\".\n", pattern, pmclass);
int modcnt = 0; int modcnt = 0;
int maxmodcnt = 100;
int maxsubcnt = 4; int maxsubcnt = 4;
int timeout = 0; int timeout = 0;
vector<Module*> mods; vector<Module*> mods;
while (modcnt < 100) while (modcnt < maxmodcnt)
{ {
int submodcnt = 0, itercnt = 0, cellcnt = 0; int submodcnt = 0, itercnt = 0, cellcnt = 0;
Module *mod = design->addModule(NEW_ID); Module *mod = design->addModule(NEW_ID);
while (modcnt < 100 && submodcnt < maxsubcnt && itercnt++ < 1000) while (modcnt < maxmodcnt && submodcnt < maxsubcnt && itercnt++ < 1000)
{ {
if (timeout++ > 10000) if (timeout++ > 10000)
log_error("pmgen generator is stuck: 10000 iterations an no matching module generated.\n"); log_error("pmgen generator is stuck: 10000 iterations an no matching module generated.\n");
@ -232,6 +251,12 @@ struct TestPmgenPass : public Pass {
log("Demo for recursive pmgen patterns. Map trees of AND/OR/XOR to $reduce_*.\n"); log("Demo for recursive pmgen patterns. Map trees of AND/OR/XOR to $reduce_*.\n");
log("\n"); 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("\n");
log(" test_pmgen -generate [options] <pattern_name>\n"); log(" test_pmgen -generate [options] <pattern_name>\n");
log("\n"); log("\n");
@ -277,6 +302,25 @@ struct TestPmgenPass : public Pass {
test_pmgen_pm(module, module->selected_cells()).run_reduce(reduce_tree); 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) void execute_generate(std::vector<std::string> args, RTLIL::Design *design)
{ {
log_header(design, "Executing TEST_PMGEN pass (-generate).\n"); log_header(design, "Executing TEST_PMGEN pass (-generate).\n");
@ -299,6 +343,9 @@ struct TestPmgenPass : public Pass {
if (pattern == "reduce") if (pattern == "reduce")
return GENERATE_PATTERN(test_pmgen_pm, reduce); return GENERATE_PATTERN(test_pmgen_pm, reduce);
if (pattern == "eqpmux")
return GENERATE_PATTERN(test_pmgen_pm, eqpmux);
if (pattern == "ice40_dsp") if (pattern == "ice40_dsp")
return GENERATE_PATTERN(ice40_dsp_pm, ice40_dsp); return GENERATE_PATTERN(ice40_dsp_pm, ice40_dsp);
@ -319,6 +366,8 @@ struct TestPmgenPass : public Pass {
return execute_reduce_chain(args, design); return execute_reduce_chain(args, design);
if (args[1] == "-reduce_tree") if (args[1] == "-reduce_tree")
return execute_reduce_tree(args, design); return execute_reduce_tree(args, design);
if (args[1] == "-eqpmux")
return execute_eqpmux(args, design);
if (args[1] == "-generate") if (args[1] == "-generate")
return execute_generate(args, design); return execute_generate(args, design);
} }

View file

@ -60,8 +60,8 @@ code portname
endcode endcode
match next match next
select nusers(port(next, \Y)) == 2
select next->type.in($_AND_, $_OR_, $_XOR_) select next->type.in($_AND_, $_OR_, $_XOR_)
select nusers(port(next, \Y)) == 2
index <IdString> next->type === first->type index <IdString> next->type === first->type
index <SigSpec> port(next, \Y) === port(first, portname) index <SigSpec> port(next, \Y) === port(first, portname)
endmatch endmatch
@ -77,8 +77,8 @@ arg first
match next match next
semioptional semioptional
select nusers(port(next, \Y)) == 2
select next->type.in($_AND_, $_OR_, $_XOR_) select next->type.in($_AND_, $_OR_, $_XOR_)
select nusers(port(next, \Y)) == 2
index <IdString> next->type === chain.back().first->type index <IdString> next->type === chain.back().first->type
index <SigSpec> port(next, \Y) === port(chain.back().first, chain.back().second) index <SigSpec> port(next, \Y) === port(chain.back().first, chain.back().second)
generate 10 generate 10
@ -104,3 +104,86 @@ finally
if (next) if (next)
chain.pop_back(); chain.pop_back();
endcode 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

View file

@ -117,6 +117,7 @@ struct SynthXilinxPass : public ScriptPass
std::string top_opt, edif_file, blif_file, family; std::string top_opt, edif_file, blif_file, family;
bool flatten, retime, vpr, ise, iopad, noiopad, noclkbuf, nobram, nolutram, nosrl, nocarry, nowidelut, abc9; bool flatten, retime, vpr, ise, iopad, noiopad, noclkbuf, nobram, nolutram, nosrl, nocarry, nowidelut, abc9;
bool flatten_before_abc;
int widemux; int widemux;
void clear_flags() YS_OVERRIDE void clear_flags() YS_OVERRIDE
@ -139,6 +140,7 @@ struct SynthXilinxPass : public ScriptPass
nocarry = false; nocarry = false;
nowidelut = false; nowidelut = false;
abc9 = false; abc9 = false;
flatten_before_abc = false;
widemux = 0; widemux = 0;
} }
@ -178,6 +180,10 @@ struct SynthXilinxPass : public ScriptPass
flatten = true; flatten = true;
continue; continue;
} }
if (args[argidx] == "-flatten_before_abc") {
flatten_before_abc = true;
continue;
}
if (args[argidx] == "-retime") { if (args[argidx] == "-retime") {
retime = true; retime = true;
continue; continue;
@ -417,6 +423,8 @@ struct SynthXilinxPass : public ScriptPass
if (check_label("map_luts")) { if (check_label("map_luts")) {
run("opt_expr -mux_undef"); run("opt_expr -mux_undef");
if (flatten_before_abc)
run("flatten");
if (help_mode) if (help_mode)
run("abc -luts 2:2,3,6:5[,10,20] [-dff]", "(option for 'nowidelut', option for '-retime')"); run("abc -luts 2:2,3,6:5[,10,20] [-dff]", "(option for 'nowidelut', option for '-retime')");
else if (abc9) { else if (abc9) {