244 lines
5.7 KiB
Python
244 lines
5.7 KiB
Python
import atexit
|
|
import getopt
|
|
import os
|
|
import subprocess
|
|
import signal
|
|
import sys
|
|
import time
|
|
import pathlib
|
|
import platform
|
|
|
|
progname = sys.argv[0]
|
|
|
|
diagnostics = False
|
|
quiet = False
|
|
verbose = False
|
|
|
|
port = 80
|
|
machine = "eecs-digital-56.mit.edu"
|
|
projectdir = "."
|
|
of = "obj"
|
|
|
|
p = False
|
|
|
|
user = "builder"
|
|
outfile = f"{of}/out.bit"
|
|
logfile = f"{of}/build.log"
|
|
|
|
synthrpt = [
|
|
"report_timing",
|
|
"report_timing_summary",
|
|
"report_utilization",
|
|
]
|
|
|
|
placerpt = synthrpt.copy()
|
|
placerpt.extend(['report_clock_utilization'])
|
|
|
|
routerpt = [
|
|
'report_drc',
|
|
'report_power',
|
|
'report_route_status',
|
|
'report_timing',
|
|
'report_timing_summary',
|
|
]
|
|
|
|
usagestr = f"""
|
|
{progname}: build SystemVerilog code remotely for 2022 6.205 labs
|
|
usage: {progname} [-dqv] [-m machine] [-p projectdir] [-o dir]
|
|
options:
|
|
-d: emit additional diagnostics during synthesis/implementation
|
|
-q: quiet: do not generate any vivado logs except for errors.
|
|
-v: be verbose (for debugging stuffs / if you see a bug)
|
|
-m: override the DNS name queried to perform the build. use with care.
|
|
-p: build the project located in projectdir (default is '.')
|
|
-o: set the output products directory (default is {of})
|
|
"""
|
|
|
|
def debuglog(s):
|
|
if verbose: print(s)
|
|
|
|
def usage():
|
|
print(usagestr)
|
|
sys.exit(1)
|
|
|
|
def getargs():
|
|
global diagnostics
|
|
global quiet
|
|
global machine
|
|
global logfile
|
|
global outfile
|
|
global projectdir
|
|
global of
|
|
global verbose
|
|
|
|
try:
|
|
opts, args = getopt.getopt(sys.argv[1:], "dm:o:p:qv")
|
|
except getopt.GetoptError as err:
|
|
print(err)
|
|
usage()
|
|
|
|
if args: usage()
|
|
for o, v in opts:
|
|
if o == '-d': diagnostics = True
|
|
elif o == '-q': quiet = True
|
|
elif o == '-m': machine = v
|
|
elif o == '-p': projectdir = v
|
|
elif o == '-o': of = v
|
|
elif o == '-v': verbose = True
|
|
else:
|
|
print(f"unrecognized option {o}")
|
|
usage()
|
|
|
|
outfile = f"{of}/out.bit"
|
|
logfile = f"{of}/build.log"
|
|
|
|
def make_posix(path):
|
|
return str(pathlib.Path(path).as_posix())
|
|
|
|
def regfiles():
|
|
ftt = {}
|
|
debuglog(f"projectdir is {projectdir}")
|
|
for dirpath, subdirs, files in os.walk(projectdir):
|
|
if 'src' not in dirpath and 'xdc' not in dirpath and 'data' not in dirpath and 'ip' not in dirpath:
|
|
continue
|
|
if dirpath.startswith("./"): dirpath = dirpath[2:]
|
|
for file in files:
|
|
fpath = os.path.join(dirpath, file)
|
|
debuglog(f"considering {fpath}")
|
|
fpath = make_posix(fpath)
|
|
|
|
if file.lower().endswith('.v'): ftt[fpath] = 'source'
|
|
elif file.lower().endswith('.sv'): ftt[fpath] = 'source'
|
|
elif file.lower().endswith('.vh'): ftt[fpath] = 'source'
|
|
elif file.lower().endswith('.svh'): ftt[fpath] = 'source'
|
|
elif file.lower().endswith('.xdc'): ftt[fpath] = 'xdc'
|
|
elif file.lower().endswith('.mem'): ftt[fpath] = 'mem'
|
|
elif file.lower().endswith('.xci'): ftt[fpath] = 'ip'
|
|
elif file.lower().endswith('.prj'): ftt[fpath] = 'mig'
|
|
|
|
debuglog(f"elaborated file list {ftt}")
|
|
return ftt
|
|
|
|
# messages are newline delineated per lab-bs.1
|
|
# utilize this to cheat a little bit
|
|
def spqsend(p, msg):
|
|
debuglog(f"writing {len(msg)} bytes over the wire")
|
|
debuglog(f"full message: {msg}")
|
|
p.stdin.write(msg + b'\n')
|
|
p.stdin.flush()
|
|
|
|
def spsend(p, msg):
|
|
debuglog(f"running {msg}")
|
|
p.stdin.write((msg + '\n').encode())
|
|
p.stdin.flush()
|
|
|
|
def sprecv(p):
|
|
l = p.stdout.readline().decode()
|
|
debuglog(f"got {l}")
|
|
return l
|
|
|
|
def xsprecv(p):
|
|
l = sprecv(p)
|
|
if (l.startswith("ERR")):
|
|
print("received unexpected server error!")
|
|
print(l)
|
|
sys.exit(1)
|
|
return l
|
|
|
|
def spstart(xargv):
|
|
debuglog(f"spawning {xargv}")
|
|
p = subprocess.PIPE
|
|
return subprocess.Popen(xargv, stdin=p, stdout=p, stderr=p)
|
|
|
|
def copyfiles(p, ftt):
|
|
for f, t in ftt.items():
|
|
fsize = os.path.getsize(f)
|
|
with open(f, 'rb') as fd:
|
|
spsend(p, f"write {f} {fsize}")
|
|
time.sleep(0.1) #?
|
|
spqsend(p, fd.read())
|
|
xsprecv(p)
|
|
|
|
spsend(p, f"type {f} {t}")
|
|
xsprecv(p)
|
|
|
|
# size message returns ... %zu bytes
|
|
def readfile(p, file, targetfile):
|
|
spsend(p, f"size {file}")
|
|
size = int(xsprecv(p).split()[-2])
|
|
spsend(p, f"read {file}")
|
|
|
|
with open(targetfile, 'wb+') as fd:
|
|
fd.write(p.stdout.read(size))
|
|
|
|
xsprecv(p)
|
|
|
|
def build(p):
|
|
cmd = "build"
|
|
if diagnostics: cmd += " -d"
|
|
if quiet: cmd += " -q"
|
|
cmd += f" obj"
|
|
|
|
print(f"Output target will be {outfile}")
|
|
|
|
spsend(p, cmd)
|
|
print("Building your code ... (this may take a while, be patient)")
|
|
result = sprecv(p)
|
|
|
|
if result.startswith("ERR"): print("Something went wrong!")
|
|
else:
|
|
readfile(p, "obj/out.bit", outfile)
|
|
print(f"Build succeeded, output at {outfile}")
|
|
|
|
readfile(p, "obj/build.log", logfile)
|
|
print(f"Log file available at {logfile}")
|
|
|
|
if (diagnostics):
|
|
for rpt in synthrpt:
|
|
readfile(p, f"obj/synthrpt_{rpt}.rpt", f"{of}/synthrpt_{rpt}.rpt")
|
|
for rpt in placerpt:
|
|
readfile(p, f"obj/placerpt_{rpt}.rpt", f"{of}/placerpt_{rpt}.rpt")
|
|
for rpt in routerpt:
|
|
readfile(p, f"obj/routerpt_{rpt}.rpt", f"{of}/routerpt_{rpt}.rpt")
|
|
print(f"Diagnostics available in {of}")
|
|
|
|
def main():
|
|
global p
|
|
getargs()
|
|
ftt = regfiles()
|
|
|
|
if not os.path.isdir(of):
|
|
print(f"output path {of} does not exist! create it or use -o?")
|
|
usage()
|
|
|
|
if platform.system() == 'Darwin' or platform.system() == 'Linux':
|
|
xargv = ['ssh', '-p', f"{port}", '-o', "StrictHostKeyChecking=no", '-o', 'UserKnownHostsFile=/dev/null']
|
|
|
|
elif platform.system() == 'Windows':
|
|
xargv = ['ssh', '-p', f"{port}", '-o', "StrictHostKeyChecking=no", '-o', 'UserKnownHostsFile=nul']
|
|
|
|
else:
|
|
raise RuntimeError('Your OS is not recognized, unsure of how to format SSH command.')
|
|
|
|
|
|
xargv.append(f"{user}@{machine}")
|
|
p = spstart(xargv)
|
|
|
|
spsend(p, "help")
|
|
result = xsprecv(p)
|
|
debuglog(result)
|
|
|
|
copyfiles(p, ftt)
|
|
build(p)
|
|
spsend(p, "exit")
|
|
p.wait()
|
|
|
|
if __name__ == "__main__":
|
|
try: main()
|
|
except (Exception, KeyboardInterrupt) as e:
|
|
if p:
|
|
debuglog("killing ssh")
|
|
os.kill(p.pid, signal.SIGINT)
|
|
p.wait()
|
|
raise e
|