1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
|
"""
Interface between Python fuzzer scripts and Lattice Diamond ispTcl
"""
from collections import defaultdict
import database
import subprocess
import tempfile
from os import path
import re
# Arc whose direction is ambiguous "---"
class AmbiguousArc:
# I
def __init__(self, lhs, rhs):
self.lhs = lhs
self.rhs = rhs
def __getitem__(self, idx):
if idx == 0:
return self.lhs
elif idx == 1:
return self.rhs
else:
raise IndexError("AmbiguousArc only connects two nets")
def __repr__(self):
return "{} --- {}".format(self.lhs, self.rhs)
def run(commands):
"""Run a list of Tcl commands, returning the output as a string"""
dtcl_path = path.join(database.get_trellis_root(), "diamond_tcl.sh")
workdir = tempfile.mkdtemp()
scriptfile = path.join(workdir, "script.tcl")
with open(scriptfile, 'w') as f:
f.write('source $::env(FOUNDRY)/data/tcltool/IspTclDev.tcl\n')
f.write('source $::env(FOUNDRY)/data/tcltool/IspTclCmd.tcl\n')
for c in commands:
f.write(c + '\n')
result = subprocess.run(["bash", dtcl_path, scriptfile], cwd=workdir).returncode
assert result == 0, "ispTcl returned non-zero status code {}".format(result)
outfile = path.join(workdir, 'ispTcl.log')
with open(outfile, 'r') as f:
output = f.read()
# Strip Lattice header
delimiter = "-" * 80
output = output[output.rindex(delimiter)+81:].strip()
# Strip Lattice pleasantry
pleasantry = "Thank you for using ispTcl."
output = output.replace(pleasantry, "").strip()
return output
# All these following commands require a tuple (ncdfile, prffile) containing
# a specimen design for the target device
def run_ncd_prf(desfiles, commands):
"""
Run a list of Tcl commands after loading given .ncd and .prf files
desfiles: a tuple (ncdfile, prffile)
commands: list of Tcl commands to run
Returns the output from IspTcl, excluding header and pleasantry
"""
run_cmds = [
"des_read_ncd {}".format(path.abspath(desfiles[0])),
"des_read_prf {}".format(path.abspath(desfiles[1]))
] + commands
result = run(run_cmds)
# Remove output of des_read_x
is_header = True
output = ""
for line in result.split('\n'):
if line.startswith("Reading preference file"):
is_header = False
elif not is_header:
output += line
output += "\n"
output = output.replace("ERROR: Placement is not performanced.", "")
return output
def get_wires_at_position(desfiles, position):
"""
Use ispTcl to get a list of wires at a given grid position
desfiles: a tuple (ncdfile, prffile)
position: a tuple (row, col)
Returns a list of tuples
(name, type, typeid)
"""
command = ["dev_list_node_at_location -row {} -col {}".format(position[0], position[1])]
result = run_ncd_prf(desfiles, command)
wires = []
for line in result.split('\n'):
sline = line.strip()
if sline == "":
continue
splitline = re.split('\s+', sline)
assert len(splitline) >= 3
wires.append((splitline[2].strip(), splitline[0].strip(), int(splitline[1])))
return wires
def get_arcs_on_wires(desfiles, wires, drivers_only=False, dir_override=dict()):
"""
Use ispTcl to get a list of arcs sinking or sourcing a list of wires
desfiles: a tuple (ncdfile, prffile)
wires: list of canonical names of the wire
drivers_only: only include arcs driving the wire in the output
dir_override: Dictionary that specificies whether a net queried by ispTcl
is a "sink" or "driver" when ispTcl returns "---" (since ISPTcl always puts
the queried net on the RHS of an an arc). dir_override is only consulted if
ispTcl returns "---" for the direction of a given net, and will
additionally override drivers_only=False for any nets specified as
"driver". Two additional strings are allowed: "ignore" to ignore "---"
connections to/from the queried net, and "mark" to return the connection as
an AmbiguousArc for later processing.
Returns a map between wire name and a list of arc tuples (source, sink)
"""
arcmap = {}
wire_idx = 0
# We can only process a limited number of nodes at a time, due to a memory leak in the Tcl API :facepalm:
for i in range(0, len(wires), 10):
subwires = wires[i:i+10]
command = []
for wire in subwires:
command += ["dev_list_arc_by_node_name -to {} -num 100000".format(wire), 'prj_list']
result = run_ncd_prf(desfiles, command)
arcs = []
for line in result.split('\n'):
sline = line.strip()
if sline == "":
pass
elif sline.startswith("MyIspProject"):
arcmap[wires[wire_idx]] = list(arcs)
wire_idx += 1
arcs = []
else:
splitline = re.split('\s+', sline)
assert len(splitline) >= 3
if splitline[1].strip() == "-->":
arcs.append((splitline[0].strip(), splitline[2].strip()))
elif splitline[1].strip() == "<--":
if not drivers_only:
arcs.append((splitline[2].strip(), splitline[0].strip()))
elif splitline[1].strip() == "---":
if isinstance(dir_override, defaultdict):
# get() overrides defaultdict behavior, and a user may
# have a valid reason to provide a default such as
# ignore.
override = dir_override[wires[wire_idx]]
else:
override = dir_override.get(wires[wire_idx], "")
if override:
if override == "sink":
arcs.append((splitline[0].strip(), splitline[2].strip()))
elif override == "driver":
arcs.append((splitline[2].strip(), splitline[0].strip()))
elif override == "mark":
arcs.append(AmbiguousArc(splitline[0].strip(), splitline[2].strip()))
elif override == "ignore":
pass
else:
assert False, ("invalid override for wire {}".
format(wires[wire_idx]))
else:
assert False, ("'---' found in ispTcl output, and no netdir_override"
" was given for {wire}. Full line:\n{line}".
format(wire=wires[wire_idx], line=line))
else:
print (splitline)
assert False, "invalid output from Tcl command `dev_list_arcs`"
return arcmap
def main():
print(run([]))
if __name__ == "__main__":
main()
|