From 73bf4539298fdc9f11302582f5e5aff57214cd28 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Thu, 15 Aug 2019 18:34:36 +0200 Subject: [PATCH 01/15] Improvements in pmgen for recursive patterns Signed-off-by: Clifford Wolf --- Makefile | 5 ++ passes/pmgen/Makefile.inc | 19 +++-- passes/pmgen/README.md | 25 ++++++- passes/pmgen/peepopt_shiftmul.pmg | 2 + passes/pmgen/pmgen.py | 116 +++++++++++++++++++++++++----- 5 files changed, 139 insertions(+), 28 deletions(-) diff --git a/Makefile b/Makefile index 95b5d451b..db8915225 100644 --- a/Makefile +++ b/Makefile @@ -487,6 +487,11 @@ define add_include_file $(eval $(call add_share_file,$(dir share/include/$(1)),$(1))) endef +define add_extra_objs +EXTRA_OBJS += $(1) +.SECONDARY: $(1) +endef + ifeq ($(PRETTY), 1) P_STATUS = 0 P_OFFSET = 0 diff --git a/passes/pmgen/Makefile.inc b/passes/pmgen/Makefile.inc index 7911132db..0d977635b 100644 --- a/passes/pmgen/Makefile.inc +++ b/passes/pmgen/Makefile.inc @@ -1,20 +1,17 @@ +%_pm.h: passes/pmgen/pmgen.py %.pmg + $(P) mkdir -p passes/pmgen && python3 $< -o $@ -p $(subst _pm.h,,$(notdir $@)) $(filter-out $<,$^) + +# -------------------------------------- + OBJS += passes/pmgen/ice40_dsp.o -OBJS += passes/pmgen/peepopt.o - -# -------------------------------------- - passes/pmgen/ice40_dsp.o: passes/pmgen/ice40_dsp_pm.h -EXTRA_OBJS += passes/pmgen/ice40_dsp_pm.h -.SECONDARY: passes/pmgen/ice40_dsp_pm.h - -passes/pmgen/ice40_dsp_pm.h: passes/pmgen/pmgen.py passes/pmgen/ice40_dsp.pmg - $(P) mkdir -p passes/pmgen && python3 $< -o $@ -p ice40_dsp $(filter-out $<,$^) +$(eval $(call add_extra_objs,passes/pmgen/ice40_dsp_pm.h)) # -------------------------------------- +OBJS += passes/pmgen/peepopt.o passes/pmgen/peepopt.o: passes/pmgen/peepopt_pm.h -EXTRA_OBJS += passes/pmgen/peepopt_pm.h -.SECONDARY: passes/pmgen/peepopt_pm.h +$(eval $(call add_extra_objs,passes/pmgen/peepopt_pm.h)) PEEPOPT_PATTERN = passes/pmgen/peepopt_shiftmul.pmg PEEPOPT_PATTERN += passes/pmgen/peepopt_muldiv.pmg diff --git a/passes/pmgen/README.md b/passes/pmgen/README.md index 2f0b1fd5a..db722c818 100644 --- a/passes/pmgen/README.md +++ b/passes/pmgen/README.md @@ -232,5 +232,28 @@ But in some cases it is more natural to utilize the implicit branch statement: portAB = \B; endcode -There is an implicit `code..endcode` block at the end of each `.pmg` file +There is an implicit `code..endcode` block at the end of each (sub)pattern that just accepts everything that gets all the way there. + +A `code..finally..endcode` block executes the code after `finally` during +back-tracking. This is useful for maintaining user data state or printing +debug messages. For example: + + udata > stack + + code + stack.push_back(addAB); + finally + stack.pop_back(); + endcode + +Declaring a subpattern +---------------------- + +A subpattern starts with a line containing the `subpattern` keyword followed +by the name of the subpattern. Subpatterns can be called from a `code` block +using a `subpattern();` C statement. + +Arguments may be passed to subpattern via state variables. The `subpattern` +line must be followed by a `arg ...` line that lists the +state variables used to pass arguments. Subpatterns allow recursion. diff --git a/passes/pmgen/peepopt_shiftmul.pmg b/passes/pmgen/peepopt_shiftmul.pmg index 6adab4e5f..d766d9e4a 100644 --- a/passes/pmgen/peepopt_shiftmul.pmg +++ b/passes/pmgen/peepopt_shiftmul.pmg @@ -34,6 +34,7 @@ match mul endmatch code +{ IdString const_factor_port = port(mul, \A).is_fully_const() ? \A : \B; IdString const_factor_signed = const_factor_port == \A ? \A_SIGNED : \B_SIGNED; Const const_factor_cnst = port(mul, const_factor_port).as_const(); @@ -91,4 +92,5 @@ code blacklist(shift); reject; +} endcode diff --git a/passes/pmgen/pmgen.py b/passes/pmgen/pmgen.py index 81052afce..22a7a5225 100644 --- a/passes/pmgen/pmgen.py +++ b/passes/pmgen/pmgen.py @@ -38,7 +38,10 @@ for a in args: assert prefix is not None current_pattern = None +current_subpattern = None patterns = dict() +subpatterns = dict() +subpattern_args = dict() state_types = dict() udata_types = dict() blocks = list() @@ -104,9 +107,12 @@ def rewrite_cpp(s): return "".join(t) -def process_pmgfile(f): +def process_pmgfile(f, filename): + linenr = 0 global current_pattern + global current_subpattern while True: + linenr += 1 line = f.readline() if line == "": break line = line.strip() @@ -119,19 +125,41 @@ def process_pmgfile(f): if current_pattern is not None: block = dict() block["type"] = "final" - block["pattern"] = current_pattern + block["pattern"] = (current_pattern, current_subpattern) blocks.append(block) line = line.split() assert len(line) == 2 assert line[1] not in patterns current_pattern = line[1] + current_subpattern = "" patterns[current_pattern] = len(blocks) + subpatterns[(current_pattern, current_subpattern)] = len(blocks) + subpattern_args[(current_pattern, current_subpattern)] = list() state_types[current_pattern] = dict() udata_types[current_pattern] = dict() continue assert current_pattern is not None + if cmd == "subpattern": + block = dict() + block["type"] = "final" + block["pattern"] = (current_pattern, current_subpattern) + blocks.append(block) + line = line.split() + assert len(line) == 2 + current_subpattern = line[1] + subpattern_args[(current_pattern, current_subpattern)] = list() + assert (current_pattern, current_subpattern) not in subpatterns + subpatterns[(current_pattern, current_subpattern)] = len(blocks) + continue + + if cmd == "arg": + line = line.split() + assert len(line) > 1 + subpattern_args[(current_pattern, current_subpattern)] += line[1:] + continue + if cmd == "state": m = re.match(r"^state\s+<(.*?)>\s+(([A-Za-z_][A-Za-z_0-9]*\s+)*[A-Za-z_][A-Za-z_0-9]*)\s*$", line) assert m @@ -155,11 +183,12 @@ def process_pmgfile(f): if cmd == "match": block = dict() block["type"] = "match" - block["pattern"] = current_pattern + block["src"] = "%s:%d" % (filename, linenr) + block["pattern"] = (current_pattern, current_subpattern) line = line.split() assert len(line) == 2 - assert line[1] not in state_types[current_pattern] + assert (line[1] not in state_types[current_pattern]) or (state_types[current_pattern][line[1]] == "Cell*") block["cell"] = line[1] state_types[current_pattern][line[1]] = "Cell*"; @@ -168,8 +197,10 @@ def process_pmgfile(f): block["index"] = list() block["filter"] = list() block["optional"] = False + block["semioptional"] = False while True: + linenr += 1 l = f.readline() assert l != "" a = l.split() @@ -201,31 +232,47 @@ def process_pmgfile(f): block["optional"] = True continue + if a[0] == "semioptional": + block["semioptional"] = True + continue + assert False + if block["optional"]: + assert not block["semioptional"] + blocks.append(block) continue if cmd == "code": block = dict() block["type"] = "code" - block["pattern"] = current_pattern + block["src"] = "%s:%d" % (filename, linenr) + block["pattern"] = (current_pattern, current_subpattern) block["code"] = list() + block["fcode"] = list() block["states"] = set() for s in line.split()[1:]: assert s in state_types[current_pattern] block["states"].add(s) + codetype = "code" + while True: + linenr += 1 l = f.readline() assert l != "" a = l.split() if len(a) == 0: continue if a[0] == "endcode": break - block["code"].append(rewrite_cpp(l.rstrip())) + if a[0] == "finally": + codetype = "fcode" + continue + + block[codetype].append(rewrite_cpp(l.rstrip())) blocks.append(block) continue @@ -234,15 +281,16 @@ def process_pmgfile(f): for fn in pmgfiles: with open(fn, "r") as f: - process_pmgfile(f) + process_pmgfile(f, fn) if current_pattern is not None: block = dict() block["type"] = "final" - block["pattern"] = current_pattern + block["pattern"] = (current_pattern, current_subpattern) blocks.append(block) current_pattern = None +current_subpattern = None if debug: pp.pprint(blocks) @@ -335,7 +383,7 @@ with open(outfile, "w") as f: print(" blacklist_dirty = false;", file=f) for index in range(len(blocks)): block = blocks[index] - if block["pattern"] != current_pattern: + if block["pattern"] != (current_pattern, current_subpattern): continue if block["type"] == "match": print(" if (st_{}.{} != nullptr && blacklist_cells.count(st_{}.{})) {{".format(current_pattern, block["cell"], current_pattern, block["cell"]), file=f) @@ -429,13 +477,21 @@ with open(outfile, "w") as f: print(" run_{}([](){{}});".format(current_pattern, current_pattern), file=f) print(" }", file=f) print("", file=f) + + for p, s in sorted(subpatterns.keys()): + print(" void block_subpattern_{}_{}() {{ block_{}(); }}".format(p, s, subpatterns[(p, s)]), file=f) + current_pattern = None + current_subpattern = None for index in range(len(blocks)): block = blocks[index] + if block["type"] in ("match", "code"): + print(" // {}".format(block["src"]), file=f) + print(" void block_{}() {{".format(index), file=f) - current_pattern = block["pattern"] + current_pattern, current_subpattern = block["pattern"] if block["type"] == "final": print(" on_accept();", file=f) @@ -449,7 +505,10 @@ with open(outfile, "w") as f: nonconst_st = set() restore_st = set() - for i in range(patterns[current_pattern], index): + for s in subpattern_args[(current_pattern, current_subpattern)]: + const_st.add(s) + + for i in range(subpatterns[(current_pattern, current_subpattern)], index): if blocks[i]["type"] == "code": for s in blocks[i]["states"]: const_st.add(s) @@ -482,6 +541,10 @@ with open(outfile, "w") as f: t = state_types[current_pattern][s] print(" {} &{} YS_ATTRIBUTE(unused) = st_{}.{};".format(t, s, current_pattern, s), file=f) + for u in sorted(udata_types[current_pattern].keys()): + t = udata_types[current_pattern][u] + print(" {} &{} YS_ATTRIBUTE(unused) = ud_{}.{};".format(t, u, current_pattern, u), file=f) + if len(restore_st): print("", file=f) for s in sorted(restore_st): @@ -490,24 +553,32 @@ with open(outfile, "w") as f: if block["type"] == "code": print("", file=f) - print(" do {", file=f) print("#define reject do {{ check_blacklist_{}(); goto rollback_label; }} while(0)".format(current_pattern), file=f) print("#define accept do {{ on_accept(); check_blacklist_{}(); if (rollback) goto rollback_label; }} while(0)".format(current_pattern), file=f) print("#define branch do {{ block_{}(); if (rollback) goto rollback_label; }} while(0)".format(index+1), file=f) + print("#define subpattern(pattern_name) block_subpattern_{}_ ## pattern_name ()".format(current_pattern), file=f) for line in block["code"]: - print(" " + line, file=f) + print(" " + line, file=f) print("", file=f) - print(" block_{}();".format(index+1), file=f) + print(" block_{}();".format(index+1), file=f) + print("#undef reject", file=f) print("#undef accept", file=f) print("#undef branch", file=f) - print(" } while (0);", file=f) + print("#undef subpattern", file=f) + print("", file=f) print("rollback_label:", file=f) print(" YS_ATTRIBUTE(unused);", file=f) + if len(block["fcode"]): + print("#define accept do {{ on_accept(); check_blacklist_{}(); }} while(0)".format(current_pattern), file=f) + for line in block["fcode"]: + print(" " + line, file=f) + print("#undef accept", file=f) + if len(restore_st) or len(nonconst_st): print("", file=f) for s in sorted(restore_st): @@ -524,12 +595,15 @@ with open(outfile, "w") as f: elif block["type"] == "match": assert len(restore_st) == 0 + print(" Cell* backup_{} = {};".format(block["cell"], block["cell"]), file=f) + if len(block["if"]): for expr in block["if"]: print("", file=f) print(" if (!({})) {{".format(expr), file=f) print(" {} = nullptr;".format(block["cell"]), file=f) print(" block_{}();".format(index+1), file=f) + print(" {} = backup_{};".format(block["cell"], block["cell"]), file=f) print(" return;", file=f) print(" }", file=f) @@ -539,16 +613,21 @@ with open(outfile, "w") as f: print(" std::get<{}>(key) = {};".format(field, entry[2]), file=f) print(" const vector &cells = index_{}[key];".format(index), file=f) + if block["semioptional"]: + print(" bool found_semioptional_match = false;", file=f) + print("", file=f) print(" for (int idx = 0; idx < GetSize(cells); idx++) {", file=f) print(" {} = cells[idx];".format(block["cell"]), file=f) print(" if (blacklist_cells.count({})) continue;".format(block["cell"]), file=f) for expr in block["filter"]: print(" if (!({})) continue;".format(expr), file=f) + if block["semioptional"]: + print(" found_semioptional_match = true;", file=f) print(" block_{}();".format(index+1), file=f) print(" if (rollback) {", file=f) print(" if (rollback != {}) {{".format(index+1), file=f) - print(" {} = nullptr;".format(block["cell"]), file=f) + print(" {} = backup_{};".format(block["cell"], block["cell"]), file=f) print(" return;", file=f) print(" }", file=f) print(" rollback = 0;", file=f) @@ -561,6 +640,11 @@ with open(outfile, "w") as f: if block["optional"]: print(" block_{}();".format(index+1), file=f) + if block["semioptional"]: + print(" if (!found_semioptional_match) block_{}();".format(index+1), file=f) + + print(" {} = backup_{};".format(block["cell"], block["cell"]), file=f) + else: assert False From 03f98d9176357b73455cf0b2c44ec3897864e893 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Thu, 15 Aug 2019 18:35:00 +0200 Subject: [PATCH 02/15] Add demo_reduce pass to demonstrace recursive pattern matching Signed-off-by: Clifford Wolf --- passes/pmgen/.gitignore | 1 + passes/pmgen/Makefile.inc | 6 +++ passes/pmgen/demo_reduce.cc | 99 ++++++++++++++++++++++++++++++++++++ passes/pmgen/demo_reduce.pmg | 81 +++++++++++++++++++++++++++++ 4 files changed, 187 insertions(+) create mode 100644 passes/pmgen/demo_reduce.cc create mode 100644 passes/pmgen/demo_reduce.pmg diff --git a/passes/pmgen/.gitignore b/passes/pmgen/.gitignore index 0ad36ea2c..a1fe9a7dc 100644 --- a/passes/pmgen/.gitignore +++ b/passes/pmgen/.gitignore @@ -1,2 +1,3 @@ +/demo_reduce_pm.h /ice40_dsp_pm.h /peepopt_pm.h diff --git a/passes/pmgen/Makefile.inc b/passes/pmgen/Makefile.inc index 0d977635b..3aaee4047 100644 --- a/passes/pmgen/Makefile.inc +++ b/passes/pmgen/Makefile.inc @@ -9,6 +9,12 @@ $(eval $(call add_extra_objs,passes/pmgen/ice40_dsp_pm.h)) # -------------------------------------- +OBJS += passes/pmgen/demo_reduce.o +passes/pmgen/demo_reduce.o: passes/pmgen/demo_reduce_pm.h +$(eval $(call add_extra_objs,passes/pmgen/demo_reduce_pm.h)) + +# -------------------------------------- + OBJS += passes/pmgen/peepopt.o passes/pmgen/peepopt.o: passes/pmgen/peepopt_pm.h $(eval $(call add_extra_objs,passes/pmgen/peepopt_pm.h)) diff --git a/passes/pmgen/demo_reduce.cc b/passes/pmgen/demo_reduce.cc new file mode 100644 index 000000000..dd8c66f56 --- /dev/null +++ b/passes/pmgen/demo_reduce.cc @@ -0,0 +1,99 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +#include "passes/pmgen/demo_reduce_pm.h" + +void create_reduce(demo_reduce_pm &pm) +{ + auto &st = pm.st_reduce; + auto &ud = pm.ud_reduce; + + if (ud.longest_chain.empty()) + return; + + log("Found chain of length %d (%s):\n", GetSize(ud.longest_chain), log_id(st.first->type)); + + SigSpec A; + SigSpec Y = ud.longest_chain.front().first->getPort(ID(Y)); + auto last_cell = ud.longest_chain.back().first; + + for (auto it : ud.longest_chain) { + auto cell = it.first; + if (cell == last_cell) { + A.append(cell->getPort(ID(A))); + A.append(cell->getPort(ID(B))); + } else { + A.append(cell->getPort(it.second == ID(A) ? ID(B) : ID(A))); + } + log(" %s\n", log_id(cell)); + pm.autoremove(cell); + } + + Cell *c; + + if (last_cell->type == ID($_AND_)) + c = pm.module->addReduceAnd(NEW_ID, A, Y); + else if (last_cell->type == ID($_OR_)) + c = pm.module->addReduceOr(NEW_ID, A, Y); + else if (last_cell->type == ID($_XOR_)) + c = pm.module->addReduceXor(NEW_ID, A, Y); + else + log_abort(); + + log(" -> %s (%s)\n", log_id(c), log_id(c->type)); +} + +struct DemoReducePass : public Pass { + DemoReducePass() : Pass("demo_reduce", "map chains of AND/OR/XOR") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" demo_reduce [options] [selection]\n"); + log("\n"); + log("Demo for recursive pmgen patterns. Map chains of AND/OR/XOR to $reduce_*.\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing DEMO_REDUCE pass.\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()) + demo_reduce_pm(module, module->selected_cells()).run_reduce(create_reduce); + } +} DemoReducePass; + +PRIVATE_NAMESPACE_END diff --git a/passes/pmgen/demo_reduce.pmg b/passes/pmgen/demo_reduce.pmg new file mode 100644 index 000000000..ddc932de5 --- /dev/null +++ b/passes/pmgen/demo_reduce.pmg @@ -0,0 +1,81 @@ +pattern reduce + +state portname +udata >> chain longest_chain +udata > non_first_cells + +code + non_first_cells.clear(); + subpattern(setup); +endcode + +match first + select first->type.in($_AND_, $_OR_, $_XOR_) + filter !non_first_cells.count(first) +endmatch + +code + longest_chain.clear(); + chain.push_back(make_pair(first, \A)); + subpattern(tail); + chain.back().second = \B; + subpattern(tail); +finally + chain.pop_back(); + log_assert(chain.empty()); +endcode + +// ------------------------------------------------------------------ + +subpattern setup + +match first + select first->type.in($_AND_, $_OR_, $_XOR_) +endmatch + +code portname + portname = \A; + branch; + portname = \B; +endcode + +match next + select nusers(port(next, \Y)) == 2 + select next->type.in($_AND_, $_OR_, $_XOR_) + index next->type === first->type + index port(next, \Y) === port(first, portname) +endmatch + +code + non_first_cells.insert(next); + reject; +endcode + +// ------------------------------------------------------------------ + +subpattern tail +arg first + +match next + semioptional + select nusers(port(next, \Y)) == 2 + select next->type.in($_AND_, $_OR_, $_XOR_) + index next->type === chain.back().first->type + index port(next, \Y) === port(chain.back().first, chain.back().second) +endmatch + +code + if (next) { + chain.push_back(make_pair(next, \A)); + subpattern(tail); + chain.back().second = \B; + subpattern(tail); + } else { + if (GetSize(chain) > GetSize(longest_chain)) + longest_chain = chain; + } + reject; +finally + if (next) + chain.pop_back(); +endcode From eb80d3d43fdb070fe99718501d3b0fbdca61ab8d Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Thu, 15 Aug 2019 22:47:59 +0200 Subject: [PATCH 03/15] Change pmgen default rule to reject, switch peepopt behavior to accept Signed-off-by: Clifford Wolf --- passes/pmgen/demo_reduce.pmg | 3 +-- passes/pmgen/ice40_dsp.pmg | 1 + passes/pmgen/peepopt_muldiv.pmg | 2 +- passes/pmgen/peepopt_shiftmul.pmg | 2 +- passes/pmgen/pmgen.py | 4 +--- 5 files changed, 5 insertions(+), 7 deletions(-) diff --git a/passes/pmgen/demo_reduce.pmg b/passes/pmgen/demo_reduce.pmg index ddc932de5..09856e9ae 100644 --- a/passes/pmgen/demo_reduce.pmg +++ b/passes/pmgen/demo_reduce.pmg @@ -23,6 +23,7 @@ code finally chain.pop_back(); log_assert(chain.empty()); + accept; endcode // ------------------------------------------------------------------ @@ -48,7 +49,6 @@ endmatch code non_first_cells.insert(next); - reject; endcode // ------------------------------------------------------------------ @@ -74,7 +74,6 @@ code if (GetSize(chain) > GetSize(longest_chain)) longest_chain = chain; } - reject; finally if (next) chain.pop_back(); diff --git a/passes/pmgen/ice40_dsp.pmg b/passes/pmgen/ice40_dsp.pmg index 1f3590d4e..7003092bb 100644 --- a/passes/pmgen/ice40_dsp.pmg +++ b/passes/pmgen/ice40_dsp.pmg @@ -159,4 +159,5 @@ code clock clock_pol clock_vld clock_pol = cp; clock_vld = true; } + accept; endcode diff --git a/passes/pmgen/peepopt_muldiv.pmg b/passes/pmgen/peepopt_muldiv.pmg index 06c275834..7cad759d0 100644 --- a/passes/pmgen/peepopt_muldiv.pmg +++ b/passes/pmgen/peepopt_muldiv.pmg @@ -32,5 +32,5 @@ code log("muldiv pattern in %s: mul=%s, div=%s\n", log_id(module), log_id(mul), log_id(div)); module->connect(div_y, val_y); autoremove(div); - reject; + accept; endcode diff --git a/passes/pmgen/peepopt_shiftmul.pmg b/passes/pmgen/peepopt_shiftmul.pmg index d766d9e4a..d4748ae19 100644 --- a/passes/pmgen/peepopt_shiftmul.pmg +++ b/passes/pmgen/peepopt_shiftmul.pmg @@ -91,6 +91,6 @@ code shift->setParam(\B_WIDTH, GetSize(new_b)); blacklist(shift); - reject; + accept; } endcode diff --git a/passes/pmgen/pmgen.py b/passes/pmgen/pmgen.py index 22a7a5225..c6e9a9cbf 100644 --- a/passes/pmgen/pmgen.py +++ b/passes/pmgen/pmgen.py @@ -494,8 +494,6 @@ with open(outfile, "w") as f: current_pattern, current_subpattern = block["pattern"] if block["type"] == "final": - print(" on_accept();", file=f) - print(" check_blacklist_{}();".format(current_pattern), file=f) print(" }", file=f) if index+1 != len(blocks): print("", file=f) @@ -556,7 +554,7 @@ with open(outfile, "w") as f: print("#define reject do {{ check_blacklist_{}(); goto rollback_label; }} while(0)".format(current_pattern), file=f) print("#define accept do {{ on_accept(); check_blacklist_{}(); if (rollback) goto rollback_label; }} while(0)".format(current_pattern), file=f) print("#define branch do {{ block_{}(); if (rollback) goto rollback_label; }} while(0)".format(index+1), file=f) - print("#define subpattern(pattern_name) block_subpattern_{}_ ## pattern_name ()".format(current_pattern), file=f) + print("#define subpattern(pattern_name) do {{ block_subpattern_{}_ ## pattern_name (); if (rollback) goto rollback_label; }} while(0)".format(current_pattern), file=f) for line in block["code"]: print(" " + line, file=f) From 969ab9027a8c4ee831e39f193f7ecb68457152c3 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Thu, 15 Aug 2019 22:48:13 +0200 Subject: [PATCH 04/15] Update pmgen documentation Signed-off-by: Clifford Wolf --- passes/pmgen/README.md | 62 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 4 deletions(-) diff --git a/passes/pmgen/README.md b/passes/pmgen/README.md index db722c818..4aa7de162 100644 --- a/passes/pmgen/README.md +++ b/passes/pmgen/README.md @@ -118,8 +118,8 @@ write matchers: connected to any of the given signal bits, plus one if any of the signal bits is also a primary input or primary output. -- In `code..endcode` blocks there exist `accept`, `reject`, and `branch` - statements. +- In `code..endcode` blocks there exist `accept`, `reject`, `branch`, and + `subpattern` statements. - In `index` statements there is a special `===` operator for the index lookup. @@ -233,7 +233,7 @@ But in some cases it is more natural to utilize the implicit branch statement: endcode There is an implicit `code..endcode` block at the end of each (sub)pattern -that just accepts everything that gets all the way there. +that just rejects. A `code..finally..endcode` block executes the code after `finally` during back-tracking. This is useful for maintaining user data state or printing @@ -243,10 +243,14 @@ debug messages. For example: code stack.push_back(addAB); + ... finally stack.pop_back(); endcode +`accept` statements can be used inside the `finally` section, but not +`reject`, `branch`, or `subpattern`. + Declaring a subpattern ---------------------- @@ -256,4 +260,54 @@ using a `subpattern();` C statement. Arguments may be passed to subpattern via state variables. The `subpattern` line must be followed by a `arg ...` line that lists the -state variables used to pass arguments. Subpatterns allow recursion. +state variables used to pass arguments. + + state foobar_type + state foobar_state + + code foobar_type foobar_state + foobar_state = false; + foobar_type = $add; + subpattern(foo); + foobar_type = $sub; + subpattern(bar); + endcode + + subpattern foo + arg foobar_type foobar_state + + match addsub + index addsub->type === foobar_type + ... + endmatch + + code + if (foobar_state) { + subpattern(tail); + } else { + foobar_state = true; + subpattern(bar); + } + endcode + + subpattern bar + arg foobar_type foobar_state + + match addsub + index addsub->type === foobar_type + ... + endmatch + + code + if (foobar_state) { + subpattern(tail); + } else { + foobar_state = true; + subpattern(foo); + } + endcode + + subpattern tail + ... + +Subpatterns cann be called recursively. From 016036f247f9538016f4e8cbdb889ac631f4a5c0 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Thu, 15 Aug 2019 23:02:37 +0200 Subject: [PATCH 05/15] Add doc for pmgen semioptional statement, Add pmgen changes to CHANGELOG Signed-off-by: Clifford Wolf --- CHANGELOG | 1 + passes/pmgen/README.md | 3 +++ 2 files changed, 4 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 638c36121..cceb9fc9f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -19,6 +19,7 @@ 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]* + - Improvements in pmgen: subpattern and recursive matches Yosys 0.8 .. Yosys 0.8-dev -------------------------- diff --git a/passes/pmgen/README.md b/passes/pmgen/README.md index 4aa7de162..f92445a86 100644 --- a/passes/pmgen/README.md +++ b/passes/pmgen/README.md @@ -175,6 +175,9 @@ explore the case where `mul` is set to `nullptr`. Without the `optional` statement a match may only be assigned nullptr when one of the `if` expressions evaluates to `false`. +The `semioptional` statement marks matches that must match if at least one +matching cell exists, but if no matching cell exists it is set to `nullptr`. + Additional code --------------- From 4a57b7e1abe148b36827a393fd7fe62e46c2b1a7 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Fri, 16 Aug 2019 11:47:51 +0200 Subject: [PATCH 06/15] Refactor demo_reduce into test_pmgen Signed-off-by: Clifford Wolf --- passes/pmgen/.gitignore | 2 +- passes/pmgen/Makefile.inc | 6 +- .../pmgen/{demo_reduce.cc => test_pmgen.cc} | 86 ++++++++++++++++--- .../pmgen/{demo_reduce.pmg => test_pmgen.pmg} | 3 + 4 files changed, 83 insertions(+), 14 deletions(-) rename passes/pmgen/{demo_reduce.cc => test_pmgen.cc} (53%) rename passes/pmgen/{demo_reduce.pmg => test_pmgen.pmg} (93%) diff --git a/passes/pmgen/.gitignore b/passes/pmgen/.gitignore index a1fe9a7dc..849b73fd4 100644 --- a/passes/pmgen/.gitignore +++ b/passes/pmgen/.gitignore @@ -1,3 +1,3 @@ -/demo_reduce_pm.h +/test_pmgen_pm.h /ice40_dsp_pm.h /peepopt_pm.h diff --git a/passes/pmgen/Makefile.inc b/passes/pmgen/Makefile.inc index 3aaee4047..bea21d971 100644 --- a/passes/pmgen/Makefile.inc +++ b/passes/pmgen/Makefile.inc @@ -9,9 +9,9 @@ $(eval $(call add_extra_objs,passes/pmgen/ice40_dsp_pm.h)) # -------------------------------------- -OBJS += passes/pmgen/demo_reduce.o -passes/pmgen/demo_reduce.o: passes/pmgen/demo_reduce_pm.h -$(eval $(call add_extra_objs,passes/pmgen/demo_reduce_pm.h)) +OBJS += passes/pmgen/test_pmgen.o +passes/pmgen/test_pmgen.o: passes/pmgen/test_pmgen_pm.h +$(eval $(call add_extra_objs,passes/pmgen/test_pmgen_pm.h)) # -------------------------------------- diff --git a/passes/pmgen/demo_reduce.cc b/passes/pmgen/test_pmgen.cc similarity index 53% rename from passes/pmgen/demo_reduce.cc rename to passes/pmgen/test_pmgen.cc index dd8c66f56..3bff9ae12 100644 --- a/passes/pmgen/demo_reduce.cc +++ b/passes/pmgen/test_pmgen.cc @@ -23,9 +23,9 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -#include "passes/pmgen/demo_reduce_pm.h" +#include "passes/pmgen/test_pmgen_pm.h" -void create_reduce(demo_reduce_pm &pm) +void reduce_chain(test_pmgen_pm &pm) { auto &st = pm.st_reduce; auto &ud = pm.ud_reduce; @@ -65,23 +65,58 @@ void create_reduce(demo_reduce_pm &pm) log(" -> %s (%s)\n", log_id(c), log_id(c->type)); } -struct DemoReducePass : public Pass { - DemoReducePass() : Pass("demo_reduce", "map chains of AND/OR/XOR") { } +void reduce_tree(test_pmgen_pm &pm) +{ + auto &st = pm.st_reduce; + auto &ud = pm.ud_reduce; + + if (ud.longest_chain.empty()) + return; + + SigSpec A = ud.leaves; + SigSpec Y = st.first->getPort(ID(Y)); + pm.autoremove(st.first); + + log("Found %s tree with %d leaves for %s (%s).\n", log_id(st.first->type), + GetSize(A), log_signal(Y), log_id(st.first)); + + Cell *c; + + if (st.first->type == ID($_AND_)) + c = pm.module->addReduceAnd(NEW_ID, A, Y); + else if (st.first->type == ID($_OR_)) + c = pm.module->addReduceOr(NEW_ID, A, Y); + else if (st.first->type == ID($_XOR_)) + c = pm.module->addReduceXor(NEW_ID, A, Y); + else + log_abort(); + + log(" -> %s (%s)\n", log_id(c), log_id(c->type)); +} + +struct TestPmgenPass : public Pass { + TestPmgenPass() : Pass("test_pmgen", "test pass for pmgen") { } void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" demo_reduce [options] [selection]\n"); + log(" test_pmgen -reduce_chain [options] [selection]\n"); log("\n"); log("Demo for recursive pmgen patterns. Map chains of AND/OR/XOR to $reduce_*.\n"); log("\n"); + log("\n"); + log(" test_pmgen -reduce_tree [options] [selection]\n"); + log("\n"); + log("Demo for recursive pmgen patterns. Map trees of AND/OR/XOR to $reduce_*.\n"); + log("\n"); } - void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + + void execute_reduce_chain(std::vector args, RTLIL::Design *design) { - log_header(design, "Executing DEMO_REDUCE pass.\n"); + log_header(design, "Executing TEST_PMGEN pass (-reduce_chain).\n"); size_t argidx; - for (argidx = 1; argidx < args.size(); argidx++) + for (argidx = 2; argidx < args.size(); argidx++) { // if (args[argidx] == "-singleton") { // singleton_mode = true; @@ -92,8 +127,39 @@ struct DemoReducePass : public Pass { extra_args(args, argidx, design); for (auto module : design->selected_modules()) - demo_reduce_pm(module, module->selected_cells()).run_reduce(create_reduce); + test_pmgen_pm(module, module->selected_cells()).run_reduce(reduce_chain); } -} DemoReducePass; + + void execute_reduce_tree(std::vector args, RTLIL::Design *design) + { + log_header(design, "Executing TEST_PMGEN pass (-reduce_tree).\n"); + + size_t argidx; + for (argidx = 2; argidx < args.size(); argidx++) + { + // if (args[argidx] == "-singleton") { + // singleton_mode = true; + // continue; + // } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + test_pmgen_pm(module, module->selected_cells()).run_reduce(reduce_tree); + } + + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + if (GetSize(args) > 1) + { + if (args[1] == "-reduce_chain") + return execute_reduce_chain(args, design); + if (args[1] == "-reduce_tree") + return execute_reduce_tree(args, design); + } + log_cmd_error("Missing or unsupported mode parameter.\n"); + } +} TestPmgenPass; PRIVATE_NAMESPACE_END diff --git a/passes/pmgen/demo_reduce.pmg b/passes/pmgen/test_pmgen.pmg similarity index 93% rename from passes/pmgen/demo_reduce.pmg rename to passes/pmgen/test_pmgen.pmg index 09856e9ae..ccb37e553 100644 --- a/passes/pmgen/demo_reduce.pmg +++ b/passes/pmgen/test_pmgen.pmg @@ -3,6 +3,7 @@ pattern reduce state portname udata >> chain longest_chain udata > non_first_cells +udata leaves code non_first_cells.clear(); @@ -15,6 +16,7 @@ match first endmatch code + leaves = SigSpec(); longest_chain.clear(); chain.push_back(make_pair(first, \A)); subpattern(tail); @@ -73,6 +75,7 @@ code } else { if (GetSize(chain) > GetSize(longest_chain)) longest_chain = chain; + leaves.append(port(chain.back().first, chain.back().second)); } finally if (next) From c710df181c2e9931ce464ea60568c864b7195e8b Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Fri, 16 Aug 2019 13:26:36 +0200 Subject: [PATCH 07/15] Add pmgen "generate" feature Signed-off-by: Clifford Wolf --- passes/pmgen/pmgen.py | 57 +++++++++++---- passes/pmgen/test_pmgen.cc | 142 ++++++++++++++++++++++++++++++++++++ passes/pmgen/test_pmgen.pmg | 22 ++++++ 3 files changed, 208 insertions(+), 13 deletions(-) diff --git a/passes/pmgen/pmgen.py b/passes/pmgen/pmgen.py index c6e9a9cbf..e252c52f5 100644 --- a/passes/pmgen/pmgen.py +++ b/passes/pmgen/pmgen.py @@ -186,6 +186,9 @@ def process_pmgfile(f, filename): block["src"] = "%s:%d" % (filename, linenr) block["pattern"] = (current_pattern, current_subpattern) + block["genargs"] = None + block["gencode"] = None + line = line.split() assert len(line) == 2 assert (line[1] not in state_types[current_pattern]) or (state_types[current_pattern][line[1]] == "Cell*") @@ -236,6 +239,19 @@ def process_pmgfile(f, filename): block["semioptional"] = True continue + if a[0] == "generate": + block["genargs"] = list([int(s) for s in a[1:]]) + block["gencode"] = list() + assert len(block["genargs"]) < 2 + while True: + linenr += 1 + l = f.readline() + assert l != "" + a = l.split() + if a[0] == "endmatch": break + block["gencode"].append(rewrite_cpp(l.rstrip())) + break + assert False if block["optional"]: @@ -310,7 +326,17 @@ with open(outfile, "w") as f: print("struct {}_pm {{".format(prefix), file=f) print(" Module *module;", file=f) print(" SigMap sigmap;", file=f) - print(" std::function on_accept;".format(prefix), file=f) + print(" std::function on_accept;", file=f) + print(" bool generate_mode;", file=f) + print("", file=f) + + print(" uint32_t rngseed;", file=f) + print(" int rng(unsigned int n) {", file=f) + print(" rngseed ^= rngseed << 13;", file=f) + print(" rngseed ^= rngseed >> 17;", file=f) + print(" rngseed ^= rngseed << 5;", file=f) + print(" return rngseed % n;", file=f) + print(" }", file=f) print("", file=f) for index in range(len(blocks)): @@ -415,7 +441,7 @@ with open(outfile, "w") as f: print("", file=f) print(" {}_pm(Module *module, const vector &cells) :".format(prefix), file=f) - print(" module(module), sigmap(module) {", file=f) + print(" module(module), sigmap(module), generate_mode(false), rngseed(12345678) {", file=f) for current_pattern in sorted(patterns.keys()): for s, t in sorted(udata_types[current_pattern].items()): if t.endswith("*"): @@ -469,17 +495,15 @@ with open(outfile, "w") as f: print(" run_{}([&](){{on_accept_f(*this);}});".format(current_pattern), file=f) print(" }", file=f) print("", file=f) - print(" void run_{}(std::function on_accept_f) {{".format(current_pattern, current_pattern), file=f) - print(" run_{}([&](){{on_accept_f(st_{});}});".format(current_pattern, current_pattern), file=f) - print(" }", file=f) - print("", file=f) print(" void run_{}() {{".format(current_pattern), file=f) print(" run_{}([](){{}});".format(current_pattern, current_pattern), file=f) print(" }", file=f) print("", file=f) - for p, s in sorted(subpatterns.keys()): - print(" void block_subpattern_{}_{}() {{ block_{}(); }}".format(p, s, subpatterns[(p, s)]), file=f) + if len(subpatterns): + for p, s in sorted(subpatterns.keys()): + print(" void block_subpattern_{}_{}() {{ block_{}(); }}".format(p, s, subpatterns[(p, s)]), file=f) + print("", file=f) current_pattern = None current_subpattern = None @@ -611,8 +635,8 @@ with open(outfile, "w") as f: print(" std::get<{}>(key) = {};".format(field, entry[2]), file=f) print(" const vector &cells = index_{}[key];".format(index), file=f) - if block["semioptional"]: - print(" bool found_semioptional_match = false;", file=f) + if block["semioptional"] or block["genargs"] is not None: + print(" bool found_any_match = false;", file=f) print("", file=f) print(" for (int idx = 0; idx < GetSize(cells); idx++) {", file=f) @@ -620,8 +644,8 @@ with open(outfile, "w") as f: 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"]: - print(" found_semioptional_match = true;", file=f) + if block["semioptional"] or block["genargs"] is not None: + print(" found_any_match = true;", file=f) print(" block_{}();".format(index+1), file=f) print(" if (rollback) {", file=f) print(" if (rollback != {}) {{".format(index+1), file=f) @@ -639,10 +663,17 @@ with open(outfile, "w") as f: print(" block_{}();".format(index+1), file=f) if block["semioptional"]: - print(" if (!found_semioptional_match) block_{}();".format(index+1), file=f) + print(" if (!found_any_match) block_{}();".format(index+1), file=f) print(" {} = backup_{};".format(block["cell"], block["cell"]), file=f) + if block["genargs"] is not None: + print(" if (generate_mode && !found_any_match) {", file=f) + if len(block["genargs"]) == 1: + print(" if (rng(100) >= {}) return;".format(block["genargs"][0]), file=f) + for line in block["gencode"]: + print(" " + line, file=f) + print(" }", file=f) else: assert False diff --git a/passes/pmgen/test_pmgen.cc b/passes/pmgen/test_pmgen.cc index 3bff9ae12..48d58b263 100644 --- a/passes/pmgen/test_pmgen.cc +++ b/passes/pmgen/test_pmgen.cc @@ -23,7 +23,12 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN +// for peepopt_pm +bool did_something; + #include "passes/pmgen/test_pmgen_pm.h" +#include "passes/pmgen/ice40_dsp_pm.h" +#include "passes/pmgen/peepopt_pm.h" void reduce_chain(test_pmgen_pm &pm) { @@ -94,6 +99,100 @@ void reduce_tree(test_pmgen_pm &pm) log(" -> %s (%s)\n", log_id(c), log_id(c->type)); } +#define GENERATE_PATTERN(pmclass, pattern) \ + generate_pattern([](pmclass &pm, std::function f){ return pm.run_ ## pattern(f); }, #pmclass, #pattern, design) + +void pmtest_addports(Module *module) +{ + pool driven_bits, used_bits; + SigMap sigmap(module); + int icnt = 0, ocnt = 0; + + for (auto cell : module->cells()) + for (auto conn : cell->connections()) + { + if (cell->input(conn.first)) + for (auto bit : sigmap(conn.second)) + used_bits.insert(bit); + if (cell->output(conn.first)) + for (auto bit : sigmap(conn.second)) + driven_bits.insert(bit); + } + + for (auto wire : vector(module->wires())) + { + SigSpec ibits, obits; + for (auto bit : sigmap(wire)) { + if (!used_bits.count(bit)) + obits.append(bit); + if (!driven_bits.count(bit)) + ibits.append(bit); + } + if (!ibits.empty()) { + Wire *w = module->addWire(stringf("\\i%d", icnt++), GetSize(ibits)); + w->port_input = true; + module->connect(ibits, w); + } + if (!obits.empty()) { + Wire *w = module->addWire(stringf("\\o%d", ocnt++), GetSize(obits)); + w->port_output = true; + module->connect(w, obits); + } + } + + module->fixup_ports(); +} + +template +void generate_pattern(std::function)> run, const char *pmclass, const char *pattern, Design *design) +{ + log("Generating \"%s\" patterns for pattern matcher \"%s\".\n", pattern, pmclass); + + int modcnt = 0; + + while (modcnt < 100) + { + int submodcnt = 0, itercnt = 0, cellcnt = 0; + Module *mod = design->addModule(NEW_ID); + + while (submodcnt < 10 && itercnt++ < 1000) + { + pm matcher(mod, mod->cells()); + + matcher.rng(1); + matcher.rngseed += modcnt; + matcher.rng(1); + matcher.rngseed += submodcnt; + matcher.rng(1); + matcher.rngseed += itercnt; + matcher.rng(1); + matcher.rngseed += cellcnt; + matcher.rng(1); + + if (GetSize(mod->cells()) != cellcnt) + { + bool found_match = false; + run(matcher, [&](){ found_match = true; }); + + if (found_match) { + Module *m = design->addModule(stringf("\\pmtest_%s_%s_%05d", + pmclass, pattern, modcnt++)); + mod->cloneInto(m); + pmtest_addports(m); + submodcnt++; + } + + cellcnt = GetSize(mod->cells()); + } + + matcher.generate_mode = true; + run(matcher, [](){}); + } + + design->remove(mod); + } +} + struct TestPmgenPass : public Pass { TestPmgenPass() : Pass("test_pmgen", "test pass for pmgen") { } void help() YS_OVERRIDE @@ -104,11 +203,18 @@ struct TestPmgenPass : public Pass { log("\n"); log("Demo for recursive pmgen patterns. Map chains of AND/OR/XOR to $reduce_*.\n"); log("\n"); + log("\n"); log(" test_pmgen -reduce_tree [options] [selection]\n"); log("\n"); log("Demo for recursive pmgen patterns. Map trees of AND/OR/XOR to $reduce_*.\n"); log("\n"); + + log("\n"); + log(" test_pmgen -generate [options] \n"); + log("\n"); + log("Create modules that match the specified pattern.\n"); + log("\n"); } void execute_reduce_chain(std::vector args, RTLIL::Design *design) @@ -149,6 +255,40 @@ struct TestPmgenPass : public Pass { test_pmgen_pm(module, module->selected_cells()).run_reduce(reduce_tree); } + void execute_generate(std::vector args, RTLIL::Design *design) + { + log_header(design, "Executing TEST_PMGEN pass (-generate).\n"); + + size_t argidx; + for (argidx = 2; argidx < args.size(); argidx++) + { + // if (args[argidx] == "-singleton") { + // singleton_mode = true; + // continue; + // } + break; + } + + if (argidx+1 != args.size()) + log_cmd_error("Expected exactly one pattern.\n"); + + string pattern = args[argidx]; + + if (pattern == "reduce") + return GENERATE_PATTERN(test_pmgen_pm, reduce); + + if (pattern == "ice40_dsp") + return GENERATE_PATTERN(ice40_dsp_pm, ice40_dsp); + + if (pattern == "peepopt-muldiv") + return GENERATE_PATTERN(peepopt_pm, muldiv); + + if (pattern == "peepopt-shiftmul") + return GENERATE_PATTERN(peepopt_pm, shiftmul); + + log_cmd_error("Unkown pattern: %s\n", pattern.c_str()); + } + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { if (GetSize(args) > 1) @@ -157,6 +297,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] == "-generate") + return execute_generate(args, design); } log_cmd_error("Missing or unsupported mode parameter.\n"); } diff --git a/passes/pmgen/test_pmgen.pmg b/passes/pmgen/test_pmgen.pmg index ccb37e553..077d337d6 100644 --- a/passes/pmgen/test_pmgen.pmg +++ b/passes/pmgen/test_pmgen.pmg @@ -13,6 +13,22 @@ endcode match first select first->type.in($_AND_, $_OR_, $_XOR_) filter !non_first_cells.count(first) +generate + SigSpec A = module->addWire(NEW_ID); + SigSpec B = module->addWire(NEW_ID); + SigSpec Y = module->addWire(NEW_ID); + switch (rng(3)) + { + case 0: + module->addAndGate(NEW_ID, A, B, Y); + break; + case 1: + module->addOrGate(NEW_ID, A, B, Y); + break; + case 2: + module->addXorGate(NEW_ID, A, B, Y); + break; + } endmatch code @@ -64,6 +80,12 @@ match next select next->type.in($_AND_, $_OR_, $_XOR_) index next->type === chain.back().first->type index port(next, \Y) === port(chain.back().first, chain.back().second) +generate 50 + SigSpec A = module->addWire(NEW_ID); + SigSpec B = module->addWire(NEW_ID); + SigSpec Y = port(chain.back().first, chain.back().second); + Cell *c = module->addAndGate(NEW_ID, A, B, Y); + c->type = chain.back().first->type; endmatch code From f45dad8220ed7c24ddaad5fc3f84bb80ddafb5ae Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Fri, 16 Aug 2019 13:47:50 +0200 Subject: [PATCH 08/15] Redesign pmgen backtracking for recursive matching Signed-off-by: Clifford Wolf --- passes/pmgen/pmgen.py | 67 ++++++++++++++++++++------------------ passes/pmgen/test_pmgen.cc | 4 ++- 2 files changed, 38 insertions(+), 33 deletions(-) diff --git a/passes/pmgen/pmgen.py b/passes/pmgen/pmgen.py index e252c52f5..95fcd2540 100644 --- a/passes/pmgen/pmgen.py +++ b/passes/pmgen/pmgen.py @@ -351,6 +351,7 @@ with open(outfile, "w") as f: print(" pool blacklist_cells;", file=f) print(" pool autoremove_cells;", file=f) print(" bool blacklist_dirty;", file=f) + print(" vector> rollback_stack;", file=f) print(" int rollback;", file=f) print("", file=f) @@ -393,6 +394,19 @@ with open(outfile, "w") as f: print(" }", file=f) print("", file=f) + print(" void check_blacklist() {", file=f) + print(" if (!blacklist_dirty)", file=f) + print(" return;", file=f) + print(" blacklist_dirty = false;", file=f) + print(" for (int i = 0; i < GetSize(rollback_stack); i++)", file=f) + print(" if (blacklist_cells.count(rollback_stack[i].first)) {", file=f) + print(" rollback = rollback_stack[i].second;", file=f) + print(" rollback_stack.resize(i);", file=f) + print(" return;", file=f) + print(" }", file=f) + print(" }", file=f) + print("", file=f) + print(" void autoremove(Cell *cell) {", file=f) print(" if (cell != nullptr) {", file=f) print(" if (blacklist_cells.insert(cell).second)", file=f) @@ -402,23 +416,6 @@ with open(outfile, "w") as f: print(" }", file=f) print("", file=f) - for current_pattern in sorted(patterns.keys()): - print(" void check_blacklist_{}() {{".format(current_pattern), file=f) - print(" if (!blacklist_dirty)", file=f) - print(" return;", file=f) - print(" blacklist_dirty = false;", file=f) - for index in range(len(blocks)): - block = blocks[index] - if block["pattern"] != (current_pattern, current_subpattern): - continue - if block["type"] == "match": - print(" if (st_{}.{} != nullptr && blacklist_cells.count(st_{}.{})) {{".format(current_pattern, block["cell"], current_pattern, block["cell"]), file=f) - print(" rollback = {};".format(index+1), file=f) - print(" return;", file=f) - print(" }", file=f) - print(" rollback = 0;", file=f) - print(" }", file=f) - print("", file=f) current_pattern = None print(" SigSpec port(Cell *cell, IdString portname) {", file=f) @@ -488,7 +485,8 @@ with open(outfile, "w") as f: print(" st_{}.{} = nullptr;".format(current_pattern, s), file=f) else: print(" st_{}.{} = {}();".format(current_pattern, s, t), file=f) - print(" block_{}();".format(patterns[current_pattern]), file=f) + print(" block_{}(1);".format(patterns[current_pattern]), file=f) + print(" log_assert(rollback_stack.empty());", file=f) print(" }", file=f) print("", file=f) print(" void run_{}(std::function on_accept_f) {{".format(current_pattern, prefix), file=f) @@ -502,7 +500,7 @@ with open(outfile, "w") as f: if len(subpatterns): for p, s in sorted(subpatterns.keys()): - print(" void block_subpattern_{}_{}() {{ block_{}(); }}".format(p, s, subpatterns[(p, s)]), file=f) + print(" void block_subpattern_{}_{}(int recursion) {{ block_{}(recursion); }}".format(p, s, subpatterns[(p, s)]), file=f) print("", file=f) current_pattern = None @@ -514,7 +512,7 @@ with open(outfile, "w") as f: if block["type"] in ("match", "code"): print(" // {}".format(block["src"]), file=f) - print(" void block_{}() {{".format(index), file=f) + print(" void block_{}(int recursion YS_ATTRIBUTE(unused)) {{".format(index), file=f) current_pattern, current_subpattern = block["pattern"] if block["type"] == "final": @@ -575,16 +573,16 @@ with open(outfile, "w") as f: if block["type"] == "code": print("", file=f) - print("#define reject do {{ check_blacklist_{}(); goto rollback_label; }} while(0)".format(current_pattern), file=f) - print("#define accept do {{ on_accept(); check_blacklist_{}(); if (rollback) goto rollback_label; }} while(0)".format(current_pattern), file=f) - print("#define branch do {{ block_{}(); if (rollback) goto rollback_label; }} while(0)".format(index+1), file=f) - print("#define subpattern(pattern_name) do {{ block_subpattern_{}_ ## pattern_name (); if (rollback) goto rollback_label; }} while(0)".format(current_pattern), file=f) + print("#define reject do { check_blacklist(); goto rollback_label; } while(0)", file=f) + print("#define accept do { on_accept(); check_blacklist(); if (rollback) goto rollback_label; } while(0)", file=f) + print("#define branch do {{ block_{}(recursion+1); if (rollback) goto rollback_label; }} while(0)".format(index+1), file=f) + print("#define subpattern(pattern_name) do {{ block_subpattern_{}_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; }} while(0)".format(current_pattern), file=f) for line in block["code"]: print(" " + line, file=f) print("", file=f) - print(" block_{}();".format(index+1), file=f) + print(" block_{}(recursion+1);".format(index+1), file=f) print("#undef reject", file=f) print("#undef accept", file=f) @@ -596,7 +594,7 @@ with open(outfile, "w") as f: print(" YS_ATTRIBUTE(unused);", file=f) if len(block["fcode"]): - print("#define accept do {{ on_accept(); check_blacklist_{}(); }} while(0)".format(current_pattern), file=f) + print("#define accept do { on_accept(); check_blacklist(); } while(0)", file=f) for line in block["fcode"]: print(" " + line, file=f) print("#undef accept", file=f) @@ -624,7 +622,7 @@ with open(outfile, "w") as f: print("", file=f) print(" if (!({})) {{".format(expr), file=f) print(" {} = nullptr;".format(block["cell"]), file=f) - print(" block_{}();".format(index+1), file=f) + print(" block_{}(recursion+1);".format(index+1), file=f) print(" {} = backup_{};".format(block["cell"], block["cell"]), file=f) print(" return;", file=f) print(" }", file=f) @@ -646,9 +644,12 @@ with open(outfile, "w") as f: print(" if (!({})) continue;".format(expr), file=f) if block["semioptional"] or block["genargs"] is not None: print(" found_any_match = true;", file=f) - print(" block_{}();".format(index+1), file=f) - print(" if (rollback) {", file=f) - print(" if (rollback != {}) {{".format(index+1), file=f) + print(" rollback_stack.push_back(make_pair(cells[idx], recursion));", file=f) + print(" block_{}(recursion+1);".format(index+1), file=f) + print(" if (rollback == 0) {", file=f) + print(" rollback_stack.pop_back();", file=f) + print(" } else {", file=f) + print(" if (rollback != recursion) {{".format(index+1), file=f) print(" {} = backup_{};".format(block["cell"], block["cell"]), file=f) print(" return;", file=f) print(" }", file=f) @@ -660,10 +661,10 @@ with open(outfile, "w") as f: print(" {} = nullptr;".format(block["cell"]), file=f) if block["optional"]: - print(" block_{}();".format(index+1), file=f) + print(" block_{}(recursion+1);".format(index+1), file=f) if block["semioptional"]: - print(" if (!found_any_match) block_{}();".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) @@ -673,6 +674,8 @@ with open(outfile, "w") as f: print(" if (rng(100) >= {}) return;".format(block["genargs"][0]), file=f) for line in block["gencode"]: print(" " + line, file=f) + print(" rollback_stack.clear();", file=f) + print(" rollback = -1;", file=f) print(" }", file=f) else: assert False diff --git a/passes/pmgen/test_pmgen.cc b/passes/pmgen/test_pmgen.cc index 48d58b263..053dbe021 100644 --- a/passes/pmgen/test_pmgen.cc +++ b/passes/pmgen/test_pmgen.cc @@ -149,13 +149,14 @@ void generate_pattern(std::function)> run, const log("Generating \"%s\" patterns for pattern matcher \"%s\".\n", pattern, pmclass); int modcnt = 0; + int maxsubcnt = 4; while (modcnt < 100) { int submodcnt = 0, itercnt = 0, cellcnt = 0; Module *mod = design->addModule(NEW_ID); - while (submodcnt < 10 && itercnt++ < 1000) + while (submodcnt < maxsubcnt && itercnt++ < 1000) { pm matcher(mod, mod->cells()); @@ -190,6 +191,7 @@ void generate_pattern(std::function)> run, const } design->remove(mod); + maxsubcnt *= 2; } } From 20910fd7c8638ec58c85e750d5b8a7da1c83cded Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Fri, 16 Aug 2019 14:16:35 +0200 Subject: [PATCH 09/15] Add pmgen finish statement, return number of matches Signed-off-by: Clifford Wolf --- passes/pmgen/README.md | 117 +++++++++++++++++++++--------------- passes/pmgen/pmgen.py | 58 ++++++++++-------- passes/pmgen/test_pmgen.cc | 2 +- passes/pmgen/test_pmgen.pmg | 5 +- 4 files changed, 108 insertions(+), 74 deletions(-) diff --git a/passes/pmgen/README.md b/passes/pmgen/README.md index f92445a86..be908ef0b 100644 --- a/passes/pmgen/README.md +++ b/passes/pmgen/README.md @@ -45,9 +45,9 @@ of type `foobar_pm::state__t`.) Similarly the `.pmg` file declares user data variables that become members of `.ud_`, a struct of type `foobar_pm::udata__t`. -There are four versions of the `run_()` method: Without callback, -callback without arguments, callback with reference to `pm`, and callback with -reference to `pm.st_`. +There are three versions of the `run_()` method: Without callback, +callback without arguments, and callback with reference to `pm`. All versions +of the `run_()` method return the number of found matches. The .pmg File Format @@ -118,8 +118,8 @@ write matchers: connected to any of the given signal bits, plus one if any of the signal bits is also a primary input or primary output. -- In `code..endcode` blocks there exist `accept`, `reject`, `branch`, and - `subpattern` statements. +- In `code..endcode` blocks there exist `accept`, `reject`, `branch`, + `finish`, and `subpattern` statements. - In `index` statements there is a special `===` operator for the index lookup. @@ -246,13 +246,13 @@ debug messages. For example: code stack.push_back(addAB); - ... + ... finally stack.pop_back(); endcode -`accept` statements can be used inside the `finally` section, but not -`reject`, `branch`, or `subpattern`. +`accept` and `finish` statements can be used inside the `finally` section, +but not `reject`, `branch`, or `subpattern`. Declaring a subpattern ---------------------- @@ -265,52 +265,75 @@ Arguments may be passed to subpattern via state variables. The `subpattern` line must be followed by a `arg ...` line that lists the state variables used to pass arguments. - state foobar_type - state foobar_state + state foobar_type + state foobar_state - code foobar_type foobar_state - foobar_state = false; - foobar_type = $add; - subpattern(foo); - foobar_type = $sub; - subpattern(bar); - endcode + code foobar_type foobar_state + foobar_state = false; + foobar_type = $add; + subpattern(foo); + foobar_type = $sub; + subpattern(bar); + endcode - subpattern foo - arg foobar_type foobar_state + subpattern foo + arg foobar_type foobar_state - match addsub - index addsub->type === foobar_type - ... - endmatch + match addsub + index addsub->type === foobar_type + ... + endmatch - code - if (foobar_state) { - subpattern(tail); - } else { - foobar_state = true; - subpattern(bar); - } - endcode + code + if (foobar_state) { + subpattern(tail); + } else { + foobar_state = true; + subpattern(bar); + } + endcode - subpattern bar - arg foobar_type foobar_state + subpattern bar + arg foobar_type foobar_state - match addsub - index addsub->type === foobar_type - ... - endmatch + match addsub + index addsub->type === foobar_type + ... + endmatch - code - if (foobar_state) { - subpattern(tail); - } else { - foobar_state = true; - subpattern(foo); - } - endcode + code + if (foobar_state) { + subpattern(tail); + } else { + foobar_state = true; + subpattern(foo); + } + endcode - subpattern tail - ... + subpattern tail + ... Subpatterns cann be called recursively. + +Generate Blocks +--------------- + +Match blocks may contain an optional `generate` section that is used for automatic +test-case generation. For example: + + match mul + ... + generate 10 + SigSpec Y = port(ff, \D); + SigSpec A = module->addWire(NEW_ID, GetSize(Y) - rng(GetSize(Y)/2)); + SigSpec B = module->addWire(NEW_ID, GetSize(Y) - rng(GetSize(Y)/2)); + module->addMul(NEW_ID, A, B, Y, rng(2)); + endmatch + +The expression `rng(n)` returns a non-negative integer less than `n`. + +The argument to `generate` is the chance of this generate block being executed +when the match block did not match anything, in percent. + +The special statement `finish` can be used within generate blocks to terminate +the current pattern matcher run. diff --git a/passes/pmgen/pmgen.py b/passes/pmgen/pmgen.py index 95fcd2540..6950a99c0 100644 --- a/passes/pmgen/pmgen.py +++ b/passes/pmgen/pmgen.py @@ -328,6 +328,7 @@ with open(outfile, "w") as f: print(" SigMap sigmap;", file=f) print(" std::function on_accept;", file=f) print(" bool generate_mode;", file=f) + print(" int accept_cnt;", file=f) print("", file=f) print(" uint32_t rngseed;", file=f) @@ -476,7 +477,8 @@ with open(outfile, "w") as f: print("", file=f) for current_pattern in sorted(patterns.keys()): - print(" void run_{}(std::function on_accept_f) {{".format(current_pattern), file=f) + print(" int run_{}(std::function on_accept_f) {{".format(current_pattern), file=f) + print(" accept_cnt = 0;", file=f) print(" on_accept = on_accept_f;", file=f) print(" rollback = 0;", file=f) print(" blacklist_dirty = false;", file=f) @@ -487,14 +489,15 @@ with open(outfile, "w") as f: print(" st_{}.{} = {}();".format(current_pattern, s, t), file=f) print(" block_{}(1);".format(patterns[current_pattern]), file=f) print(" log_assert(rollback_stack.empty());", file=f) + print(" return accept_cnt;", file=f) print(" }", file=f) print("", file=f) - print(" void run_{}(std::function on_accept_f) {{".format(current_pattern, prefix), file=f) - print(" run_{}([&](){{on_accept_f(*this);}});".format(current_pattern), file=f) + print(" int run_{}(std::function on_accept_f) {{".format(current_pattern, prefix), file=f) + print(" return run_{}([&](){{on_accept_f(*this);}});".format(current_pattern), file=f) print(" }", file=f) print("", file=f) - print(" void run_{}() {{".format(current_pattern), file=f) - print(" run_{}([](){{}});".format(current_pattern, current_pattern), file=f) + print(" int run_{}() {{".format(current_pattern), file=f) + print(" return run_{}([](){{}});".format(current_pattern, current_pattern), file=f) print(" }", file=f) print("", file=f) @@ -574,7 +577,8 @@ with open(outfile, "w") as f: if block["type"] == "code": print("", file=f) print("#define reject do { check_blacklist(); goto rollback_label; } while(0)", file=f) - print("#define accept do { on_accept(); check_blacklist(); if (rollback) goto rollback_label; } while(0)", file=f) + print("#define accept do { accept_cnt++; on_accept(); check_blacklist(); if (rollback) goto rollback_label; } while(0)", file=f) + print("#define finish do { rollback = -1; rollback_stack.clean(); goto rollback_label; } while(0)", file=f) print("#define branch do {{ block_{}(recursion+1); if (rollback) goto rollback_label; }} while(0)".format(index+1), file=f) print("#define subpattern(pattern_name) do {{ block_subpattern_{}_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; }} while(0)".format(current_pattern), file=f) @@ -586,6 +590,7 @@ with open(outfile, "w") as f: print("#undef reject", file=f) print("#undef accept", file=f) + print("#undef finish", file=f) print("#undef branch", file=f) print("#undef subpattern", file=f) @@ -594,10 +599,12 @@ with open(outfile, "w") as f: print(" YS_ATTRIBUTE(unused);", file=f) if len(block["fcode"]): - print("#define accept do { on_accept(); check_blacklist(); } while(0)", file=f) + print("#define accept do { accept_cnt++; on_accept(); check_blacklist(); } while(0)", file=f) + print("#define finish do { rollback = -1; rollback_stack.clean(); return; } while(0)", file=f) for line in block["fcode"]: print(" " + line, file=f) print("#undef accept", file=f) + print("#undef finish", file=f) if len(restore_st) or len(nonconst_st): print("", file=f) @@ -631,29 +638,32 @@ with open(outfile, "w") as f: print(" index_{}_key_type key;".format(index), file=f) for field, entry in enumerate(block["index"]): print(" std::get<{}>(key) = {};".format(field, entry[2]), file=f) - print(" const vector &cells = index_{}[key];".format(index), file=f) + print(" auto cells_ptr = index_{}.find(key);".format(index), file=f) if block["semioptional"] or block["genargs"] is not None: print(" bool found_any_match = false;", file=f) print("", file=f) - print(" for (int idx = 0; idx < GetSize(cells); idx++) {", file=f) - print(" {} = cells[idx];".format(block["cell"]), file=f) - print(" if (blacklist_cells.count({})) continue;".format(block["cell"]), file=f) + print(" if (cells_ptr != index_{}.end()) {{".format(index), file=f) + print(" const vector &cells = cells_ptr->second;".format(index), file=f) + print(" for (int idx = 0; idx < GetSize(cells); idx++) {", file=f) + print(" {} = cells[idx];".format(block["cell"]), file=f) + print(" if (blacklist_cells.count({})) continue;".format(block["cell"]), file=f) for expr in block["filter"]: - print(" if (!({})) continue;".format(expr), file=f) + print(" if (!({})) continue;".format(expr), file=f) if block["semioptional"] or block["genargs"] is not None: - print(" found_any_match = true;", file=f) - print(" rollback_stack.push_back(make_pair(cells[idx], recursion));", file=f) - print(" block_{}(recursion+1);".format(index+1), file=f) - print(" if (rollback == 0) {", file=f) - print(" rollback_stack.pop_back();", file=f) - print(" } else {", file=f) - print(" if (rollback != recursion) {{".format(index+1), file=f) - print(" {} = backup_{};".format(block["cell"], block["cell"]), file=f) - print(" return;", file=f) + print(" found_any_match = true;", file=f) + print(" rollback_stack.push_back(make_pair(cells[idx], recursion));", file=f) + print(" block_{}(recursion+1);".format(index+1), file=f) + print(" if (rollback == 0) {", file=f) + print(" rollback_stack.pop_back();", file=f) + print(" } else {", file=f) + print(" if (rollback != recursion) {{".format(index+1), file=f) + print(" {} = backup_{};".format(block["cell"], block["cell"]), file=f) + print(" return;", file=f) + print(" }", file=f) + print(" rollback = 0;", file=f) print(" }", file=f) - print(" rollback = 0;", file=f) print(" }", file=f) print(" }", file=f) @@ -669,14 +679,14 @@ with open(outfile, "w") as f: print(" {} = backup_{};".format(block["cell"], block["cell"]), file=f) if block["genargs"] is not None: + print("#define finish do { rollback = -1; rollback_stack.clean(); return; } while(0)", file=f) print(" if (generate_mode && !found_any_match) {", file=f) if len(block["genargs"]) == 1: print(" if (rng(100) >= {}) return;".format(block["genargs"][0]), file=f) for line in block["gencode"]: print(" " + line, file=f) - print(" rollback_stack.clear();", file=f) - print(" rollback = -1;", file=f) print(" }", file=f) + print("#undef finish", file=f) else: assert False diff --git a/passes/pmgen/test_pmgen.cc b/passes/pmgen/test_pmgen.cc index 053dbe021..d787d49be 100644 --- a/passes/pmgen/test_pmgen.cc +++ b/passes/pmgen/test_pmgen.cc @@ -235,7 +235,7 @@ struct TestPmgenPass : public Pass { extra_args(args, argidx, design); for (auto module : design->selected_modules()) - test_pmgen_pm(module, module->selected_cells()).run_reduce(reduce_chain); + while (test_pmgen_pm(module, module->selected_cells()).run_reduce(reduce_chain)) {} } void execute_reduce_tree(std::vector args, RTLIL::Design *design) diff --git a/passes/pmgen/test_pmgen.pmg b/passes/pmgen/test_pmgen.pmg index 077d337d6..211477a62 100644 --- a/passes/pmgen/test_pmgen.pmg +++ b/passes/pmgen/test_pmgen.pmg @@ -41,7 +41,8 @@ code finally chain.pop_back(); log_assert(chain.empty()); - accept; + if (GetSize(longest_chain) > 1) + accept; endcode // ------------------------------------------------------------------ @@ -80,7 +81,7 @@ match next select next->type.in($_AND_, $_OR_, $_XOR_) index next->type === chain.back().first->type index port(next, \Y) === port(chain.back().first, chain.back().second) -generate 50 +generate 10 SigSpec A = module->addWire(NEW_ID); SigSpec B = module->addWire(NEW_ID); SigSpec Y = port(chain.back().first, chain.back().second); From 64bd414e54aadc1446e114cf95f33dd95f5cf291 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Fri, 16 Aug 2019 14:35:13 +0200 Subject: [PATCH 10/15] Minor bugfix in "test_pmgen -generate" Signed-off-by: Clifford Wolf --- passes/pmgen/test_pmgen.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passes/pmgen/test_pmgen.cc b/passes/pmgen/test_pmgen.cc index d787d49be..5f7bf2189 100644 --- a/passes/pmgen/test_pmgen.cc +++ b/passes/pmgen/test_pmgen.cc @@ -156,7 +156,7 @@ void generate_pattern(std::function)> run, const int submodcnt = 0, itercnt = 0, cellcnt = 0; Module *mod = design->addModule(NEW_ID); - while (submodcnt < maxsubcnt && itercnt++ < 1000) + while (modcnt < 100 && submodcnt < maxsubcnt && itercnt++ < 1000) { pm matcher(mod, mod->cells()); From cd5a372cd18fe71cfa8428be59e355d82f34000d Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Fri, 16 Aug 2019 13:00:12 -0700 Subject: [PATCH 11/15] Add help() call --- passes/pmgen/test_pmgen.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/passes/pmgen/test_pmgen.cc b/passes/pmgen/test_pmgen.cc index 5f7bf2189..94e59f62e 100644 --- a/passes/pmgen/test_pmgen.cc +++ b/passes/pmgen/test_pmgen.cc @@ -302,6 +302,7 @@ struct TestPmgenPass : public Pass { if (args[1] == "-generate") return execute_generate(args, design); } + help(); log_cmd_error("Missing or unsupported mode parameter.\n"); } } TestPmgenPass; From f95853c8228ec8310a1142fe29eea85b765ea3b4 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Sat, 17 Aug 2019 11:29:37 +0200 Subject: [PATCH 12/15] Add pmgen "fallthrough" statement Signed-off-by: Clifford Wolf --- passes/pmgen/README.md | 3 +++ passes/pmgen/pmgen.py | 17 ++++++++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/passes/pmgen/README.md b/passes/pmgen/README.md index be908ef0b..5f6a8ab1b 100644 --- a/passes/pmgen/README.md +++ b/passes/pmgen/README.md @@ -315,6 +315,9 @@ state variables used to pass arguments. Subpatterns cann be called recursively. +If a `subpattern` statement is preceded by a `fallthrough` statement, this is +equivalent to calling the subpattern at the end of the preceding block. + Generate Blocks --------------- diff --git a/passes/pmgen/pmgen.py b/passes/pmgen/pmgen.py index 6950a99c0..8401e1295 100644 --- a/passes/pmgen/pmgen.py +++ b/passes/pmgen/pmgen.py @@ -141,12 +141,23 @@ def process_pmgfile(f, filename): assert current_pattern is not None - if cmd == "subpattern": + if cmd == "fallthrough": block = dict() - block["type"] = "final" - block["pattern"] = (current_pattern, current_subpattern) + block["type"] = "fallthrough" blocks.append(block) line = line.split() + assert len(line) == 1 + continue + + if cmd == "subpattern": + if len(blocks) == 0 or blocks[-1]["type"] != "fallthrough": + block = dict() + block["type"] = "final" + block["pattern"] = (current_pattern, current_subpattern) + blocks.append(block) + elif len(blocks) and blocks[-1]["type"] == "fallthrough": + del blocks[-1] + line = line.split() assert len(line) == 2 current_subpattern = line[1] subpattern_args[(current_pattern, current_subpattern)] = list() From 318ae0351cdce550cd2f7bf8f5cccc3ecf97cb60 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Sat, 17 Aug 2019 13:53:55 +0200 Subject: [PATCH 13/15] Improvements in "test_pmgen -generate" Signed-off-by: Clifford Wolf --- passes/pmgen/test_pmgen.cc | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/passes/pmgen/test_pmgen.cc b/passes/pmgen/test_pmgen.cc index 94e59f62e..9f42a95d0 100644 --- a/passes/pmgen/test_pmgen.cc +++ b/passes/pmgen/test_pmgen.cc @@ -150,6 +150,8 @@ void generate_pattern(std::function)> run, const int modcnt = 0; int maxsubcnt = 4; + int timeout = 0; + vector mods; while (modcnt < 100) { @@ -158,6 +160,9 @@ void generate_pattern(std::function)> run, const while (modcnt < 100 && submodcnt < maxsubcnt && itercnt++ < 1000) { + if (timeout++ > 10000) + log_error("pmgen generator is stuck: 10000 iterations an no matching module generated.\n"); + pm matcher(mod, mod->cells()); matcher.rng(1); @@ -174,25 +179,40 @@ void generate_pattern(std::function)> run, const { bool found_match = false; run(matcher, [&](){ found_match = true; }); + cellcnt = GetSize(mod->cells()); if (found_match) { Module *m = design->addModule(stringf("\\pmtest_%s_%s_%05d", pmclass, pattern, modcnt++)); + log("Creating module %s with %d cells.\n", log_id(m), cellcnt); mod->cloneInto(m); pmtest_addports(m); + mods.push_back(m); submodcnt++; + timeout = 0; } - - cellcnt = GetSize(mod->cells()); } matcher.generate_mode = true; run(matcher, [](){}); } + if (submodcnt) + maxsubcnt *= 2; + design->remove(mod); - maxsubcnt *= 2; } + + Module *m = design->addModule(stringf("\\pmtest_%s_%s", pmclass, pattern)); + log("Creating module %s with %d cells.\n", log_id(m), GetSize(mods)); + for (auto mod : mods) { + Cell *c = m->addCell(mod->name, mod->name); + for (auto port : mod->ports) { + Wire *w = m->addWire(NEW_ID, GetSize(mod->wire(port))); + c->setPort(port, w); + } + } + pmtest_addports(m); } struct TestPmgenPass : public Pass { From f3405fb04878dc030e82ad49a1c12d7fd0b489f0 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Sat, 17 Aug 2019 13:54:18 +0200 Subject: [PATCH 14/15] Refactor pmgen rollback mechanism Signed-off-by: Clifford Wolf --- passes/pmgen/pmgen.py | 53 +++++++++++++++++-------------------------- 1 file changed, 21 insertions(+), 32 deletions(-) diff --git a/passes/pmgen/pmgen.py b/passes/pmgen/pmgen.py index 8401e1295..18c3bf5a5 100644 --- a/passes/pmgen/pmgen.py +++ b/passes/pmgen/pmgen.py @@ -362,8 +362,7 @@ with open(outfile, "w") as f: print(" dict> sigusers;", file=f) print(" pool blacklist_cells;", file=f) print(" pool autoremove_cells;", file=f) - print(" bool blacklist_dirty;", file=f) - print(" vector> rollback_stack;", file=f) + print(" dict rollback_cache;", file=f) print(" int rollback;", file=f) print("", file=f) @@ -399,31 +398,20 @@ with open(outfile, "w") as f: print("", file=f) print(" void blacklist(Cell *cell) {", file=f) - print(" if (cell != nullptr) {", file=f) - print(" if (blacklist_cells.insert(cell).second)", file=f) - print(" blacklist_dirty = true;", file=f) + print(" if (cell != nullptr && blacklist_cells.insert(cell).second) {", file=f) + print(" auto ptr = rollback_cache.find(cell);", file=f) + print(" if (ptr == rollback_cache.end()) return;", file=f) + print(" int rb = ptr->second;", file=f) + print(" if (rollback == 0 || rollback > rb)", file=f) + print(" rollback = rb;", file=f) print(" }", file=f) print(" }", file=f) print("", file=f) - print(" void check_blacklist() {", file=f) - print(" if (!blacklist_dirty)", file=f) - print(" return;", file=f) - print(" blacklist_dirty = false;", file=f) - print(" for (int i = 0; i < GetSize(rollback_stack); i++)", file=f) - print(" if (blacklist_cells.count(rollback_stack[i].first)) {", file=f) - print(" rollback = rollback_stack[i].second;", file=f) - print(" rollback_stack.resize(i);", file=f) - print(" return;", file=f) - print(" }", file=f) - print(" }", file=f) - print("", file=f) - print(" void autoremove(Cell *cell) {", file=f) print(" if (cell != nullptr) {", file=f) - print(" if (blacklist_cells.insert(cell).second)", file=f) - print(" blacklist_dirty = true;", file=f) print(" autoremove_cells.insert(cell);", file=f) + print(" blacklist(cell);", file=f) print(" }", file=f) print(" }", file=f) print("", file=f) @@ -492,14 +480,13 @@ with open(outfile, "w") as f: print(" accept_cnt = 0;", file=f) print(" on_accept = on_accept_f;", file=f) print(" rollback = 0;", file=f) - print(" blacklist_dirty = false;", file=f) for s, t in sorted(state_types[current_pattern].items()): if t.endswith("*"): print(" st_{}.{} = nullptr;".format(current_pattern, s), file=f) else: print(" st_{}.{} = {}();".format(current_pattern, s, t), file=f) print(" block_{}(1);".format(patterns[current_pattern]), file=f) - print(" log_assert(rollback_stack.empty());", file=f) + print(" log_assert(rollback_cache.empty());", file=f) print(" return accept_cnt;", file=f) print(" }", file=f) print("", file=f) @@ -587,9 +574,9 @@ with open(outfile, "w") as f: if block["type"] == "code": print("", file=f) - print("#define reject do { check_blacklist(); goto rollback_label; } while(0)", file=f) - print("#define accept do { accept_cnt++; on_accept(); check_blacklist(); if (rollback) goto rollback_label; } while(0)", file=f) - print("#define finish do { rollback = -1; rollback_stack.clean(); goto rollback_label; } while(0)", file=f) + print("#define reject do { goto rollback_label; } while(0)", file=f) + print("#define accept do { accept_cnt++; on_accept(); if (rollback) goto rollback_label; } while(0)", file=f) + print("#define finish do { rollback = -1; goto rollback_label; } while(0)", file=f) print("#define branch do {{ block_{}(recursion+1); if (rollback) goto rollback_label; }} while(0)".format(index+1), file=f) print("#define subpattern(pattern_name) do {{ block_subpattern_{}_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; }} while(0)".format(current_pattern), file=f) @@ -610,10 +597,12 @@ with open(outfile, "w") as f: print(" YS_ATTRIBUTE(unused);", file=f) if len(block["fcode"]): - print("#define accept do { accept_cnt++; on_accept(); check_blacklist(); } while(0)", file=f) - print("#define finish do { rollback = -1; rollback_stack.clean(); return; } while(0)", file=f) + print("#define accept do { accept_cnt++; on_accept(); } while(0)", file=f) + print("#define finish do { rollback = -1; goto finish_label; } while(0)", file=f) for line in block["fcode"]: print(" " + line, file=f) + print("finish_label:", file=f) + print(" YS_ATTRIBUTE(unused);", file=f) print("#undef accept", file=f) print("#undef finish", file=f) @@ -664,11 +653,11 @@ with open(outfile, "w") as f: print(" if (!({})) continue;".format(expr), file=f) if block["semioptional"] or block["genargs"] is not None: print(" found_any_match = true;", file=f) - print(" rollback_stack.push_back(make_pair(cells[idx], recursion));", file=f) + print(" auto rollback_ptr = rollback_cache.insert(make_pair(cells[idx], recursion));", file=f) print(" block_{}(recursion+1);".format(index+1), file=f) - print(" if (rollback == 0) {", file=f) - print(" rollback_stack.pop_back();", file=f) - print(" } else {", file=f) + print(" if (rollback_ptr.second)", file=f) + print(" rollback_cache.erase(rollback_ptr.first);", file=f) + print(" if (rollback) {", file=f) print(" if (rollback != recursion) {{".format(index+1), file=f) print(" {} = backup_{};".format(block["cell"], block["cell"]), file=f) print(" return;", file=f) @@ -690,7 +679,7 @@ with open(outfile, "w") as f: print(" {} = backup_{};".format(block["cell"], block["cell"]), file=f) if block["genargs"] is not None: - print("#define finish do { rollback = -1; rollback_stack.clean(); 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) if len(block["genargs"]) == 1: print(" if (rng(100) >= {}) return;".format(block["genargs"][0]), file=f) From f20be90436b32e853d68c7e102a65d43f3843d91 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Sat, 17 Aug 2019 14:05:10 +0200 Subject: [PATCH 15/15] Add test for pmtest_test "reduce" demo pattern Signed-off-by: Clifford Wolf --- tests/various/pmgen_reduce.ys | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 tests/various/pmgen_reduce.ys diff --git a/tests/various/pmgen_reduce.ys b/tests/various/pmgen_reduce.ys new file mode 100644 index 000000000..c214d3f25 --- /dev/null +++ b/tests/various/pmgen_reduce.ys @@ -0,0 +1,21 @@ +test_pmgen -generate reduce +hierarchy -top pmtest_test_pmgen_pm_reduce +flatten; opt_clean + +design -save gold +test_pmgen -reduce_chain +design -stash gate + +design -copy-from gold -as gold pmtest_test_pmgen_pm_reduce +design -copy-from gate -as gate pmtest_test_pmgen_pm_reduce +miter -equiv -flatten -make_assert gold gate miter +sat -verify -prove-asserts miter + +design -load gold +test_pmgen -reduce_tree +design -stash gate + +design -copy-from gold -as gold pmtest_test_pmgen_pm_reduce +design -copy-from gate -as gate pmtest_test_pmgen_pm_reduce +miter -equiv -flatten -make_assert gold gate miter +sat -verify -prove-asserts miter