Progress in pmgen

Signed-off-by: Clifford Wolf <clifford@clifford.at>
This commit is contained in:
Clifford Wolf 2019-01-13 10:57:11 +01:00
parent ad69c668ce
commit b9545aa0e1
5 changed files with 347 additions and 8 deletions

1
passes/pmgen/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/ice40_dsp_pm.h

View File

@ -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

65
passes/pmgen/ice40_dsp.cc Normal file
View File

@ -0,0 +1,65 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
#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<std::string> 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

View File

@ -1,6 +1,6 @@
state SigBit clock
state bool clock_pol, clock_vld
state SigSpec sigA, sigB, sigY
state <SigBit> clock
state <bool> clock_pol clock_vld
state <SigSpec> 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 <SigSpec> 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 <SigSpec> 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 <SigSpec> 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) {

262
passes/pmgen/pmgen.py Normal file
View File

@ -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<void(struct {}_pm*)> 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_{}_key_type, vector<Cell*>> index_{};".format(index, index), file=f)
print(" pool<Cell*> 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<void(struct {}_pm*)> 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)