manta/examples/counter/lab-bc.py

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