From b9545aa0e1260f3d7123ec63e9dcfb0e022e2ff3 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Sun, 13 Jan 2019 10:57:11 +0100 Subject: [PATCH] Progress in pmgen Signed-off-by: Clifford Wolf --- passes/pmgen/.gitignore | 1 + passes/pmgen/Makefile.inc | 8 ++ passes/pmgen/ice40_dsp.cc | 65 +++++++++ passes/pmgen/ice40_dsp.pmg | 19 +-- passes/pmgen/pmgen.py | 262 +++++++++++++++++++++++++++++++++++++ 5 files changed, 347 insertions(+), 8 deletions(-) create mode 100644 passes/pmgen/.gitignore create mode 100644 passes/pmgen/Makefile.inc create mode 100644 passes/pmgen/ice40_dsp.cc create mode 100644 passes/pmgen/pmgen.py diff --git a/passes/pmgen/.gitignore b/passes/pmgen/.gitignore new file mode 100644 index 000000000..c9263057e --- /dev/null +++ b/passes/pmgen/.gitignore @@ -0,0 +1 @@ +/ice40_dsp_pm.h diff --git a/passes/pmgen/Makefile.inc b/passes/pmgen/Makefile.inc new file mode 100644 index 000000000..33baaca30 --- /dev/null +++ b/passes/pmgen/Makefile.inc @@ -0,0 +1,8 @@ +OBJS += passes/pmgen/ice40_dsp.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/ice40_dsp.pmg passes/pmgen/pmgen.py + $(P) cd passes/pmgen && python3 pmgen.py ice40_dsp diff --git a/passes/pmgen/ice40_dsp.cc b/passes/pmgen/ice40_dsp.cc new file mode 100644 index 000000000..0498f31b7 --- /dev/null +++ b/passes/pmgen/ice40_dsp.cc @@ -0,0 +1,65 @@ +/* + * 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" +#include "passes/pmgen/ice40_dsp_pm.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +void ice40_dsp_accept(ice40_dsp_pm * /* pm */) +{ +} + +struct Ice40DspPass : public Pass { + Ice40DspPass() : Pass("ice40_dsp", "iCE40: map multipliers") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" ice40_dsp [options] [selection]\n"); + log("\n"); + log("Map multipliers and iCE40 DSP resources.\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing ICE40_DSP pass (map multipliers).\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()) + { + ice40_dsp_pm pm(module); + pm.run(ice40_dsp_accept); + } + } +} Ice40DspPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/pmgen/ice40_dsp.pmg b/passes/pmgen/ice40_dsp.pmg index 5a0af3e04..2b9bb8783 100644 --- a/passes/pmgen/ice40_dsp.pmg +++ b/passes/pmgen/ice40_dsp.pmg @@ -1,6 +1,6 @@ -state SigBit clock -state bool clock_pol, clock_vld -state SigSpec sigA, sigB, sigY +state clock +state clock_pol clock_vld +state sigA sigB sigY match mul select mul->type.in($mul) @@ -10,7 +10,8 @@ endmatch match ffA select ffA->type.in($dff) - filter port(ffA, \Q) === port(mul, \A) + select nusers(port(ffA, \Q)) == 2 + filter port(ffA, \Q) === port(mul, \A) optional endmatch @@ -28,11 +29,12 @@ endcode match ffB select ffB->type.in($dff) - filter port(ffB, \Q) === port(mul, \B) + select nusers(port(ffA, \Q)) == 2 + filter port(ffB, \Q) === port(mul, \B) optional endmatch -code sigB clock clok_pol clock_vld +code sigB clock clock_pol clock_vld sigB = port(mul, \B); if (ffB != nullptr) { @@ -51,11 +53,12 @@ endcode match ffY select ffY->type.in($dff) - filter port(ffY, \D) === port(mul, \Y) + select nusers(port(ffY, \D)) == 2 + filter port(ffY, \D) === port(mul, \Y) optional endmatch -code sigY clock clok_pol clock_vld +code sigY clock clock_pol clock_vld sigY = port(mul, \Y); if (ffY != nullptr) { diff --git a/passes/pmgen/pmgen.py b/passes/pmgen/pmgen.py new file mode 100644 index 000000000..bf70a6dbd --- /dev/null +++ b/passes/pmgen/pmgen.py @@ -0,0 +1,262 @@ +#!/usr/bin/env python3 + +import re +import sys +import pprint + +pp = pprint.PrettyPrinter(indent=4) + +prefix = sys.argv[1] + +state_types = dict() +blocks = list() +ids = dict() + +def rewrite_cpp(s): + t = list() + i = 0 + while i < len(s): + if s[i] in ("'", '"') and i + 1 < len(s): + j = i + 1 + while j + 1 < len(s) and s[j] != s[i]: + if s[j] == '\\' and j + 1 < len(s): + j += 1 + j += 1 + t.append(s[i:j+1]) + i = j + 1 + continue + + if s[i] in ('$', '\\') and i + 1 < len(s): + j = i + 1 + while True: + if j == len(s): + j -= 1 + break + if ord('a') <= ord(s[j]) <= ord('z'): + j += 1 + continue + if ord('A') <= ord(s[j]) <= ord('Z'): + j += 1 + continue + if ord('0') <= ord(s[j]) <= ord('9'): + j += 1 + continue + if s[j] == '_': + j += 1 + continue + j -= 1 + break + + n = s[i:j+1] + i = j + 1 + + if n[0] == '$': + v = "id_d_" + n[1:] + else: + v = "id_b_" + n[1:] + + if v not in ids: + ids[v] = n + else: + assert ids[v] == n + + t.append(v) + continue + + if s[i] == "\t": + t.append(" ") + else: + t.append(s[i]) + + i += 1 + + return "".join(t) + +with open("%s.pmg" % prefix, "r") as f: + while True: + line = f.readline() + if line == "": break + line = line.strip() + + cmd = line.split() + if len(cmd) == 0: continue + cmd = cmd[0] + + 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 + type_str = m.group(1) + states_str = m.group(2) + for s in re.split(r"\s+", states_str): + assert s not in state_types + state_types[s] = type_str + continue + + if cmd == "match": + block = dict() + block["type"] = "match" + + line = line.split() + assert len(line) == 2 + assert line[1] not in state_types + block["cell"] = line[1] + state_types[line[1]] = "Cell*"; + + block["select"] = list() + block["filter"] = list() + block["optional"] = False + + while True: + l = f.readline() + assert l != "" + a = l.split() + if len(a) == 0: continue + if a[0] == "endmatch": break + + if a[0] == "select": + b = l.lstrip()[6:] + block["select"].append(rewrite_cpp(b.strip())) + continue + + if a[0] == "filter": + m = re.match(r"^\s*filter\s+<(.*?)>\s+(.*?)\s*===\s*(.*?)\s*$", l) + assert m + block["filter"].append((m.group(1), rewrite_cpp(m.group(2)), rewrite_cpp(m.group(3)))) + continue + + if a[0] == "optional": + block["optional"] = True + continue + + assert False + + blocks.append(block) + + if cmd == "code": + block = dict() + block["type"] = "code" + block["code"] = list() + block["states"] = set() + + for s in line.split()[1:]: + assert s in state_types + block["states"].add(s) + + while True: + 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())) + + blocks.append(block) + +# pp.pprint(blocks) + +with open("%s_pm.h" % prefix, "w") as f: + print("// Generated by pmgen.py from {}.pgm".format(prefix), file=f) + print("#include \"kernel/yosys.h\"", file=f) + print("#include \"kernel/sigtools.h\"", file=f) + print("YOSYS_NAMESPACE_BEGIN", file=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("", file=f) + + for index in range(len(blocks)): + block = blocks[index] + if block["type"] == "match": + index_types = list() + for filt in block["filter"]: + index_types.append(filt[0]) + print(" typedef std::tuple<{}> index_{}_key_type;".format(", ".join(index_types), index), file=f) + print(" dict> index_{};".format(index, index), file=f) + print(" pool blacklist;", file=f) + print("", file=f) + + print(" struct state_t {", file=f) + + for s, t in sorted(state_types.items()): + print(" {} {};".format(t, s), file=f) + print(" } st;", file=f) + print("", file=f) + + for v, n in sorted(ids.items()): + if n[0] == "\\": + print(" IdString {}{{\"\\{}\"}};".format(v, n), file=f) + else: + print(" IdString {}{{\"{}\"}};".format(v, n), file=f) + print("", file=f) + + print(" {}_pm(Module *module) :".format(prefix), file=f) + print(" module(module), sigmap(module) {", file=f) + print(" }", file=f) + + print("", file=f) + print(" void run(std::function on_accept_f) {{".format(prefix), file=f) + print(" on_accept = on_accept_f;", file=f) + if len(blocks): + print(" block_0();", file=f) + print(" }", file=f) + + for index in range(len(blocks)): + block = blocks[index] + + print("", file=f) + print(" void block_{}() {{".format(index), file=f) + + const_st = set() + nonconst_st = set() + restore_st = set() + + for i in range(index): + if blocks[i]["type"] == "code": + for s in blocks[i]["states"]: + const_st.add(s) + elif blocks[i]["type"] == "match": + const_st.add(blocks[i]["cell"]) + else: + assert False + + if block["type"] == "code": + for s in block["states"]: + if s in const_st: + const_st.remove(s) + restore_st.add(s) + nonconst_st.add(s) + elif block["type"] == "match": + s = block["cell"] + assert s not in const_st + nonconst_st.add(s) + else: + assert False + + for s in sorted(const_st): + t = state_types[s] + if t.endswith("*"): + print(" {} const &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f) + else: + print(" const {} &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f) + + for s in sorted(nonconst_st): + t = state_types[s] + print(" {} &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f) + + if len(restore_st): + print("", file=f) + for s in sorted(restore_st): + t = state_types[s] + print(" {} backup_{} = st.{};".format(t, s, s), file=f) + + if len(restore_st): + print("", file=f) + for s in sorted(restore_st): + t = state_types[s] + print(" st.{} = backup_{};".format(s, s), file=f) + + print(" }", file=f) + + print("};\nYOSYS_NAMESPACE_END", file=f) -- 2.30.2