Create remotesyn files

Signed-off-by: Joppe Blondel <joppe@blondel.nl>
This commit is contained in:
2022-09-03 14:59:21 +02:00
parent da4b296f0d
commit 02813a4d00
11 changed files with 1566 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*__pycache__
*egg-info

21
LICENCE Normal file
View File

@ -0,0 +1,21 @@
Copyright (c) 2022, Joppe Blondel
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

34
remotesyn/Heartbeat.py Normal file
View File

@ -0,0 +1,34 @@
import threading
import time
import base64
class Heartbeat(threading.Thread):
def __init__(self, channel):
threading.Thread.__init__(self)
self.channel = channel
self.running = True
self.printing = False
def stop(self):
self.running = False
def run(self):
while self.running:
if self.printing:
print('.', end='', flush=True)
self.channel.exec_command(base64.encodebytes(b'hb'))
time.sleep(2)
class HeartbeatChecker(threading.Thread):
def __init__(self, server):
threading.Thread.__init__(self)
self.server = server
self.running = True
self.hb = True
def stop(self):
self.running = False
def run(self):
while self.running:
if not self.hb:
self.server.active = False
self.server.event.set()
self.hb = False
time.sleep(5)

200
remotesyn/__init__.py Normal file
View File

@ -0,0 +1,200 @@
from .exec_ISE import exec_ISE
from .exec_VIVADO import exec_VIVADO
from .exec_REMOTE import exec_REMOTE
from .Heartbeat import Heartbeat, HeartbeatChecker
import sys
import os
import shutil
import subprocess
import paramiko
import socket
import threading
import struct
import base64
class Executer(threading.Thread):
def __init__(self, args, channel, identifier):
threading.Thread.__init__(self)
self.args = args
self.channel = channel
self.identifier = identifier
self.pid = None
def run(self):
self.pid = subprocess.Popen(f"../{sys.argv[0]} -l -c project.cfg {self.args}", shell=True, cwd=f'{self.identifier}', stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
res = self.pid.wait()
self.pid = None
self.channel.sendall(struct.pack('>I', res))
class FileTransferSF(threading.Thread):
def __init__(self, channel, fname, identifier, fsize):
threading.Thread.__init__(self)
self.channel = channel
self.fname = fname
self.identifier = identifier
self.fsize = fsize
def run(self):
with open(f"{self.identifier}/{self.fname}", 'wb') as f:
fsize = self.fsize
while fsize>0:
fdata = self.channel.recv(1024)
f.write(fdata)
fsize -= 1024
class FileTransferRF(threading.Thread):
def __init__(self, channel, fname, identifier):
threading.Thread.__init__(self)
self.channel = channel
self.fname = fname
self.identifier = identifier
def run(self):
with open(f"{self.identifier}/{self.fname}", 'rb') as f:
stat = os.fstat(f.fileno())
print(' -> fsize', stat.st_size)
fsize = struct.pack('>q', stat.st_size)
i = stat.st_size
self.channel.sendall(b'OK'+fsize)
while i>0:
fdata = f.read(1024)
self.channel.sendall(fdata)
i -= 1024
class Server(paramiko.ServerInterface):
def __init__(self, authorized):
self.event = threading.Event()
self.authorized = authorized
self.active = True
self.hbchecker = HeartbeatChecker(self)
self.hbchecker.start()
self.processes = []
def stopall(self):
print("Stop all running processes")
for p in self.processes:
if p.pid is not None:
p.pid.terminate()
def check_channel_request(self, kind, chanid):
if kind == 'session':
return paramiko.OPEN_SUCCEEDED
def check_auth_publickey(self, username, key):
keyascii = key.get_base64()
for auth in self.authorized:
authascii = auth.split(' ')[1]
if authascii==keyascii:
return paramiko.AUTH_SUCCESSFUL
return paramiko.AUTH_FAILED
def get_allowed_auths(self, username):
return 'publickey'
def rstr(self, b):
l = struct.unpack('>I', b[:4])[0]
return (bytes.decode(b[4:4+l], 'utf-8'), b[4+l:])
def sstr(self, s):
return struct.pack('>I', len(s)) + s.encode('utf-8')
def check_channel_exec_request(self, channel, command):
# self.event.set()
command = base64.decodebytes(command)
cmd = command[:2]
data = command[2:]
if cmd==b'id':
identifier = struct.unpack('>q', data[:8])[0]
self.identifier = str(identifier)
print('>', identifier)
# Create directory
if os.path.exists(str(identifier)):
shutil.rmtree(str(identifier))
os.mkdir(str(identifier))
elif cmd==b'ex':
print('<', self.identifier)
#shutil.rmtree(str(self.identifier))
self.active = False
self.hbchecker.stop()
self.event.set()
elif cmd==b'hb':
self.hbchecker.hb = True
# List files
elif cmd==b'ls':
dr, data = self.rstr(data)
print('ls', dr)
if not os.path.exists(f"{self.identifier}/{dr}"):
channel.sendall(b'ERFile not found')
es = []
for f in os.listdir(f'{self.identifier}/{dr}'):
if os.path.isfile(f'{self.identifier}/{dr}/{f}'):
df = 'f'
else:
df = 'd'
es.append(f'{df}{f}')
channel.sendall(b'OK' + self.sstr('\n'.join(es)))
# Send file
elif cmd==b'sf':
fname, data = self.rstr(data)
fsize = struct.unpack('>q', data)[0]
print('>>', fname, fsize)
os.makedirs(os.path.dirname(f"{self.identifier}/{fname}"), exist_ok=True)
channel.sendall(b'OK\n')
FileTransferSF(channel, fname, self.identifier, fsize).start()
# Receive file
elif cmd==b'rf':
fname, data = self.rstr(data)
print('<<', fname)
if not os.path.exists(f"{self.identifier}/{fname}"):
channel.sendall(b'ERFile not found')
else:
FileTransferRF(channel, fname, self.identifier).start()
# Execute synth
elif cmd==b'do':
args, data = self.rstr(data)
print('[]', args)
executer = Executer(args, channel, self.identifier)
executer.start()
self.processes.append(executer)
return True
class Connection(threading.Thread):
def __init__(self, client, addr, host_key, authorized):
threading.Thread.__init__(self)
self.client = client
self.addr = addr
self.host_key = host_key
self.running = True
self.authorized = authorized
print(f"Connection from {addr}")
def stop(self):
self.running = False
self.server.event.set()
def run(self):
self.running = True
t = paramiko.Transport(self.client)
t.set_gss_host(socket.getfqdn(""))
t.load_server_moduli()
t.add_server_key(self.host_key)
server = Server(self.authorized)
t.start_server(server=server)
self.server = server
# Wait for the event
while server.active:
server.event.wait(10)
server.stopall()
shutil.rmtree(server.identifier)
t.close()
print('connection closed')

349
remotesyn/exec_ISE.py Normal file
View File

@ -0,0 +1,349 @@
import os
import shutil
import subprocess
import time
class exec_ISE:
def __init__(self, config, builddir):
self.config = config
self.builddir = builddir
self.create_builddir()
def create_outdir(self, target):
self.outdir = self.config.get('project', 'out_dir', fallback='OUT')
if not os.path.exists(self.outdir):
os.mkdir(self.outdir)
if not os.path.exists(f'{self.outdir}/{target}'):
os.mkdir(f'{self.outdir}/{target}')
def create_builddir(self):
if not os.path.exists(self.builddir):
os.mkdir(self.builddir)
def enter_builddir(self):
self.curdir = os.getcwd()
os.chdir(self.builddir)
def leave_builddir(self):
os.chdir(self.curdir)
def do_ip_gen(self, target):
# get used IP's for target
ips = self.config.get(target, 'src_ip', fallback='').split()
self.create_outdir(target)
print("+ Generate IPs")
for i, ip in enumerate(ips):
# Create cgp file
with open(f'{self.builddir}/coregen_{i}.cgp', 'w') as f:
f.write(f'SET busformat = BusFormatAngleBracketNotRipped\n')
f.write(f'SET designentry = VHDL\n')
f.write(f'SET device = {self.config.get("target", "device")}\n')
f.write(f'SET devicefamily = {self.config.get("target", "family")}\n')
f.write(f'SET package = {self.config.get("target", "package")}\n')
f.write(f'SET speedgrade = {self.config.get("target", "speedgrade")}\n')
f.write(f'SET flowvendor = Other\n')
f.write(f'SET verilogsim = true\n')
f.write(f'SET vhdlsim = true\n')
# crete xco file
with open(f'{self.builddir}/coregen_{i}.xco', 'w') as f:
ipsec = 'ip_%s'%ip
f.write(f'SELECT {ip} {self.config.get(ipsec, ipsec)}\n')
for s in self.config[ipsec]:
if s==ipsec:
continue
f.write(f'CSET {s}={self.config.get(ipsec, s)}\n')
f.write('GENERATE')
# Clear log
if os.path.exists(f'{self.builddir}/coregen.log'):
os.remove(f'{self.builddir}/coregen.log')
# Run coregen
pid = subprocess.Popen(f'coregen -p coregen_{i}.cgp -b coregen_{i}.xco', shell=True, cwd=self.builddir, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
while pid.poll() is None:
print('.', end='', flush=True)
time.sleep(2)
res = pid.returncode
print('')
if not os.path.exists(f'{self.outdir}/{target}/{ip}'):
os.mkdir(f'{self.outdir}/{target}/{ip}')
# Copy files to output directory if succeeded
if res == 0:
shutil.copyfile(f'{self.builddir}/{ip}.vhd', f'{self.outdir}/{target}/{ip}/{ip}.vhd')
shutil.copyfile(f'{self.builddir}/{ip}.v', f'{self.outdir}/{target}/{ip}/{ip}.v')
shutil.copyfile(f'{self.builddir}/{ip}.ngc', f'{self.outdir}/{target}/{ip}/{ip}.ngc')
# Copy log
shutil.copyfile(f'{self.builddir}/coregen.log', f'{self.outdir}/{target}/{ip}/{ip}.log')
if res!=0:
exit(res)
def do_synthesize(self, target):
self.create_outdir(target)
self.enter_builddir()
print('+ Synthesize')
extra_opts = self.config.get(target, 'extra_options').split('\n')
opts = {}
for o in extra_opts:
tp = o.split()[0]
op = ' '.join(o.split()[1:])
opts[tp] = op
if 'xst' not in opts:
opts['xst'] = ''
if 'netgen' not in opts:
opts['netgen'] = ''
with open('syn.prj', 'w') as f:
src = self.config.get(target, 'src_vhdl', fallback='').split()
for s in src:
f.write(f'vhdl work "{self.curdir}/{s}"\n')
src = self.config.get(target, 'src_verilog', fallback='').split()
for s in src:
f.write(f'verilog work "{self.curdir}/{s}"\n')
src = self.config.get(target, 'src_sysverilog', fallback='').split()
for s in src:
f.write(f'verilog work "{self.curdir}/{s}"\n')
src = self.config.get(target, 'src_ip', fallback='').split()
for s in src:
f.write(f'vhdl work "{self.curdir}/{self.outdir}/{target}/{s}/{s}.vhd"\n')
with open('prj.scr', 'w') as f:
f.write('run\n-ifn syn.prj\n-ofn syn.ngc\n-ifmt mixed\n')
f.write(f"-top {self.config.get(target, 'toplevel', fallback='_top_')}\n")
f.write(f"-p {self.config.get('target', 'device', fallback='_d_')}")
f.write(self.config.get('target', 'speedgrade', fallback='_s_'))
f.write(f"-{self.config.get('target', 'package', fallback='_p_')}")
f.write(f"\n{opts['xst']}\n")
pid = subprocess.Popen(f'xst -intstyle xflow -ifn prj.scr', shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
while pid.poll() is None:
print('.', end='', flush=True)
time.sleep(2)
res = pid.returncode
print('')
if res!=0:
self.leave_builddir()
exit(res)
success = True
pid = subprocess.Popen(f"netgen -intstyle xflow -sim -ofmt verilog -w -insert_glbl true {opts['netgen']} syn.ngc", shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
while pid.poll() is None:
print('.', end='', flush=True)
time.sleep(2)
res = pid.returncode
print('')
if res!=0:
success = False
exit(res)
self.leave_builddir()
if success:
if not os.path.exists(f'{self.outdir}/{target}'):
os.mkdir(f'{self.outdir}/{target}')
# Copy files to output directory if succeeded
shutil.copyfile(f'{self.builddir}/syn.ngc', f'{self.outdir}/{target}/{target}.ngc')
shutil.copyfile(f'{self.builddir}/syn.v', f'{self.outdir}/{target}/{target}.v')
shutil.copyfile(f'{self.builddir}/prj.srp', f'{self.outdir}/{target}/syn.log')
def do_implement(self, target):
self.create_outdir(target)
self.enter_builddir()
print('+ Implement')
extra_opts = self.config.get(target, 'extra_options').split('\n')
opts = {}
for o in extra_opts:
tp = o.split()[0]
op = ' '.join(o.split()[1:])
opts[tp] = op
if 'ngd' not in opts:
opts['ngd'] = ''
if 'map' not in opts:
opts['map'] = ''
if 'par' not in opts:
opts['par'] = ''
part = f"{self.config.get('target', 'device', fallback='_d_')}{self.config.get('target', 'speedgrade', fallback='_s_')}-{self.config.get('target', 'package', fallback='_p_')}"
cons = self.config.get(target, 'src_constraints', fallback='__con__')
pid = subprocess.Popen(f"ngdbuild -intstyle xflow -p {part} -uc {self.curdir}/{cons} {opts['ngd']} {self.curdir}/{self.outdir}/{target}/{target}.ngc impl.ngd", shell=True, stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
while pid.poll() is None:
print('.', end='', flush=True)
time.sleep(2)
res = pid.returncode
print('')
shutil.copyfile(f'impl.bld', f'{self.curdir}/{self.outdir}/{target}/impl-ngd.log')
if res!=0:
self.leave_builddir()
exit(res)
pid = subprocess.Popen(f"map -intstyle xflow -detail -p {part} {opts['map']} -w impl.ngd -o impl.map.ncd impl.pcf", shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
while pid.poll() is None:
print('.', end='', flush=True)
time.sleep(2)
res = pid.returncode
print('')
shutil.copyfile(f'impl.map.mrp', f'{self.curdir}/{self.outdir}/{target}/impl-map.log')
if res!=0:
self.leave_builddir()
exit(res)
pid = subprocess.Popen(f"par -intstyle xflow {opts['par']} -w impl.map.ncd impl.pcf | tee impl.par.log", shell=True, stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
while pid.poll() is None:
print('.', end='', flush=True)
time.sleep(2)
res = pid.returncode
print('')
shutil.copyfile(f'impl.par.log', f'{self.curdir}/{self.outdir}/{target}/impl-par.log')
if res!=0:
self.leave_builddir()
exit(res)
pid = subprocess.Popen(f"netgen -intstyle xflow -sim -ofmt verilog -w -insert_glbl true -sdf_anno true {opts['netgen']} impl.map.ncd", shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
while pid.poll() is None:
print('.', end='', flush=True)
time.sleep(2)
res = pid.returncode
print('')
if res!=0:
self.leave_builddir()
exit(res)
self.leave_builddir()
shutil.copyfile(f'{self.builddir}/impl.map.v', f'{self.outdir}/{target}/{target}.map.v')
shutil.copyfile(f'{self.builddir}/impl.map.sdf', f'{self.outdir}/{target}/{target}.map.sdf')
shutil.copyfile(f'{self.builddir}/impl.pcf.ncd', f'{self.outdir}/{target}/{target}.ncd')
shutil.copyfile(f'{self.builddir}/impl.pcf', f'{self.outdir}/{target}/{target}.pcf')
def do_bit(self, target):
self.create_outdir(target)
self.enter_builddir()
print('+ Generate output files')
extra_opts = self.config.get(target, 'extra_options').split('\n')
opts = {}
for o in extra_opts:
tp = o.split()[0]
op = ' '.join(o.split()[1:])
opts[tp] = op
if 'bitgen' not in opts:
opts['bitgen'] = ''
if 'trce' not in opts:
opts['trce'] = ''
pid = subprocess.Popen(f"bitgen -intstyle xflow -g Binary:Yes -w {opts['bitgen']} {self.curdir}/{self.outdir}/{target}/{target}.ncd bit.bit {self.curdir}/{self.outdir}/{target}/{target}.pcf", shell=True, stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
while pid.poll() is None:
print('.', end='', flush=True)
time.sleep(2)
res = pid.returncode
print('')
self.leave_builddir()
shutil.copyfile(f'{self.builddir}/bit.bit', f'{self.outdir}/{target}/{target}.bit')
shutil.copyfile(f'{self.builddir}/bit.bin', f'{self.outdir}/{target}/{target}.bin')
if res!=0:
exit(res)
self.enter_builddir()
pid = subprocess.Popen(f"trce -intstyle xflow {opts['trce']} {self.curdir}/{self.outdir}/{target}/{target}.ncd {self.curdir}/{self.outdir}/{target}/{target}.pcf", shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
while pid.poll() is None:
print('.', end='', flush=True)
time.sleep(2)
res = pid.returncode
print('')
self.leave_builddir()
shutil.copyfile(f'{self.builddir}/{target}.twr', f'{self.outdir}/{target}/timing.log')
if res!=0:
exit(res)
def do_floorplan(self, target):
self.create_outdir(target)
self.enter_builddir()
part = f"{self.config.get('target', 'device', fallback='_d_')}{self.config.get('target', 'package', fallback='_p_')}{self.config.get('target', 'speedgrade', fallback='_s_')}"
cons = f"{self.curdir}/{self.config.get(target, 'src_constraints', fallback='__con__')}"
with open('paproj.tcl', 'w') as f:
f.write(f'create_project -name paproj -dir paproj -part {part} -force\n')
f.write('set_property design_mode GateLvl [get_property srcset [current_run -impl]]\n')
f.write(f"set_property edif_top_file {self.curdir}/{self.outdir}/{target}/{target}.ngc [get_property srcset [current_run]]\n")
f.write(f"add_files [list {{{cons}}}] -fileset [get_property constrset [current_run]]\n")
f.write(f"set_property target_constrs_file {cons} [current_fileset -constrset]\n")
f.write(f"link_design\nread_xdl -file {self.curdir}/{self.outdir}/{target}/{target}.ncd\n")
pid = subprocess.Popen('planAhead -source paproj.tcl', shell=True) #, stdout=subprocess.DEVNULL, stderr=None)
res = pid.wait()
self.leave_builddir()
def do_sim(self, target):
self.create_outdir(target)
self.enter_builddir()
print('+ Simulate')
if os.path.exists('isim'):
shutil.rmtree('isim')
with open('sim.prj', 'w') as f:
src = self.config.get(target, 'src_vhdl', fallback='').split()
for s in src:
f.write(f'vhdl work "{self.curdir}/{s}"\n')
src = self.config.get(target, 'src_verilog', fallback='').split()
for s in src:
f.write(f'verilog work "{self.curdir}/{s}"\n')
src = self.config.get(target, 'src_sysverilog', fallback='').split()
for s in src:
f.write(f'verilog work "{self.curdir}/{s}"\n')
# TODO add IP
extras = ''
if self.config.get(target, 'simtype', fallback='presim') == 'postsim':
extras = f"--{self.config.get(target, 'delay', fallback='typ')}delay work.glbl"
pid = subprocess.Popen(f"fuse {extras} work.{self.config.get(target, 'toplevel', fallback='toplevel')} -prj sim.prj -o sim", shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
while pid.poll() is None:
print('.', end='', flush=True)
time.sleep(2)
res = pid.returncode
print('')
if res!=0:
self.leave_builddir()
shutil.copyfile(f'{self.builddir}/fuse.log', f'{self.outdir}/{target}/synth.log')
exit(res)
with open('sim.tcl', 'w') as f:
f.write("onerror {resume}\n")
f.write("vcd dumpfile sim.vcd\n")
f.write(f"vcd dumpvars -m {self.config.get(target, 'toplevel', fallback='toplevel')} -l {self.config.get(target, 'levels', fallback='10')}\n")
f.write("vcd dumpon\n")
f.write(f"run {self.config.get(target, 'runtime', fallback='100 ns')}\n")
f.write("vcd dumpflush\nquit\n")
extras = ''
if self.config.get(target, 'simtype', fallback='presim') == 'postsim':
extras = f"-sdf{self.config.get(target, 'delay', fallback='typ')} {self.config.get(target, 'sdfroot', fallback='dut')}={self.curdir}/{self.config.get(target, 'src_sdf', fallback='_s_')}"
pid = subprocess.Popen(f'./sim -tclbatch sim.tcl {extras} > sim.log', shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
while pid.poll() is None:
print('.', end='', flush=True)
time.sleep(2)
res = pid.returncode
print('')
if res!=0:
exit(res)
self.leave_builddir()
if not os.path.exists(f'{self.outdir}/{target}'):
os.mkdir(f'{self.outdir}/{target}')
shutil.copyfile(f'{self.builddir}/sim.vcd', f'{self.outdir}/{target}/output.vcd')
shutil.copyfile(f'{self.builddir}/fuse.log', f'{self.outdir}/{target}/synth.log')
shutil.copyfile(f'{self.builddir}/sim.log', f'{self.outdir}/{target}/output.log')

295
remotesyn/exec_REMOTE.py Normal file
View File

@ -0,0 +1,295 @@
import os
import paramiko
import struct
import base64
from .Heartbeat import Heartbeat
class exec_REMOTE:
def __init__(self, config, configfile):
self.config = config
self.configfile = configfile
self.privkey = self.config.get('server', 'privkey', fallback='__privkey__')
self.pubkey = self.config.get('server', 'pubkey', fallback='__pubkey__')
self.hostname = self.config.get('server', 'hostname', fallback='__hostname__')
self.port = self.config.get('server', 'port', fallback='__port__')
self.tc = self.config.get('project', 'toolchain', fallback='ISE')
if self.privkey=='__privkey__' or self.pubkey=='__pubkey__' or self.hostname=='__hostname__' or self.port=='__port__':
print("Not enough server information in the config file")
exit(1)
self.host_key = paramiko.RSAKey(filename=self.privkey)
client = paramiko.SSHClient()
client.load_system_host_keys()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
trans = paramiko.Transport((self.hostname, int(self.port)))
trans.connect(None, pkey=self.host_key)
self.channel = trans.open_channel('session')
self.hbchannel = trans.open_channel('session')
self.heartbeat = Heartbeat(self.hbchannel)
self.heartbeat.start()
# Send project identification
cmd = b'id' + struct.pack('>q', hash(self.host_key.get_base64()))
self.channel.exec_command(base64.encodebytes(cmd))
def __del__(self):
self.heartbeat.stop()
self.channel.exec_command(base64.encodebytes(b'ex'))
def cmd(self, cmd):
self.channel.exec_command(base64.encodebytes(cmd))
def sstr(self, s):
return struct.pack('>I', len(s)) + s.encode('utf-8')
def rstr(self):
l = struct.unpack('>I', self.channel.recv(4))[0]
return bytes.decode(self.channel.recv(l), 'utf-8')
def recv_dir(self, dr):
self.cmd(b'ls'+self.sstr(dr))
status = self.channel.recv(2)
if status!=b'OK':
msg = self.channel.recv(1024)
print("Error:", bytes.decode(msg, 'ascii'))
exit(1)
ls = self.rstr()
for p in ls.split('\n'):
tp = p[0]
name = p[1:]
if tp=='d':
self.recv_dir(f'{dr}/{name}')
else:
self.recv_file(f'{dr}/{name}')
def send_file(self, file, othername=None):
print(f"> {file}")
if not os.path.exists(file):
print(f"Error: {file} does not exists")
with open(file, 'rb') as f:
stat = os.fstat(f.fileno())
print(' -> fsize', stat.st_size)
if othername is None:
othername = file
fsize = struct.pack('>q', stat.st_size)
self.cmd(b'sf'+self.sstr(othername)+fsize)
status = self.channel.recv(3)
if status!=b'OK\n':
print('Something went wrong...')
exit(1)
i = stat.st_size
while i>0:
fdata = f.read(1024)
i -= 1024
self.channel.sendall(fdata)
def recv_file(self, file):
print(f"< {file}")
if os.path.dirname(file) != '':
os.makedirs(os.path.dirname(file), exist_ok=True)
with open(file, 'wb') as f:
self.cmd(b'rf'+self.sstr(file))
status = self.channel.recv(2)
if status!=b'OK':
msg = self.channel.recv(1024)
print("Error:", bytes.decode(msg, 'ascii'))
exit(1)
fsize = self.channel.recv(8)
fsize = struct.unpack('>q', fsize)[0]
print(' -> fsize', fsize)
while fsize>0:
f.write(self.channel.recv(1024))
fsize -= 1024
def do_ip_gen(self, target):
print("+ Generate IPs")
self.send_file(self.configfile, 'project.cfg')
self.heartbeat.printing = True
self.cmd(b'do'+self.sstr(f'ip {target}'))
res = struct.unpack('>I', self.channel.recv(4))[0]
self.heartbeat.printing = False
print(f' [{res}]')
# get used IP's for target
ips = self.config.get(target, 'src_ip', fallback='').split()
for i, ip in enumerate(ips):
self.recv_dir(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/{ip}")
if res != 0:
print("Some error occured...")
exit(1)
def do_synthesize(self, target):
print("+ Synthesize")
self.send_file(self.configfile, 'project.cfg')
src = self.config.get(target, 'src_vhdl', fallback='').split()
for s in src:
self.send_file(s)
src = self.config.get(target, 'src_verilog', fallback='').split()
for s in src:
self.send_file(s)
src = self.config.get(target, 'src_sysverilog', fallback='').split()
for s in src:
self.send_file(s)
src = self.config.get(target, 'src_ip', fallback='').split()
for s in src:
if self.tc=='ISE':
self.send_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/{s}/{s}.vhd")
elif self.tc=="VIVADO":
self.send_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/{s}/{s}.xci")
if self.tc=="VIVADO":
self.send_file(f"{self.config.get(target, 'src_constraints', fallback='__con__')}")
self.heartbeat.printing = True
self.cmd(b'do'+self.sstr(f'syn {target}'))
res = struct.unpack('>I', self.channel.recv(4))[0]
self.heartbeat.printing = False
print(f' [{res}]')
if res != 0:
print("Some error occured...")
if self.tc=='ISE':
self.recv_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/syn.log")
elif self.tc=="VIVADO":
self.recv_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/synth.log")
exit(1)
if self.tc=='ISE':
self.recv_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/{target}.ngc")
self.recv_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/{target}.v")
self.recv_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/syn.log")
elif self.tc=="VIVADO":
self.recv_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/synth.log")
self.recv_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/post_synth.dcp")
self.recv_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/synth_netlist.v")
self.recv_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/synth_netlist.sdf")
def do_implement(self, target):
print("+ Implement")
self.send_file(self.configfile, 'project.cfg')
if self.tc=='ISE':
self.send_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/{target}.ngc")
self.send_file(f"{self.config.get(target, 'src_constraints', fallback='__con__')}")
elif self.tc=="VIVADO":
self.send_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/post_synth.dcp")
self.heartbeat.printing = True
self.cmd(b'do'+self.sstr(f'impl {target}'))
res = struct.unpack('>I', self.channel.recv(4))[0]
self.heartbeat.printing = False
print(f' [{res}]')
if res != 0:
print("Some error occured...")
if self.tc=='ISE':
# FIXME possible that not all are there
self.recv_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/impl-ngd.log")
self.recv_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/impl-map.log")
self.recv_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/impl-par.log")
elif self.tc=="VIVADO":
self.recv_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/impl.log")
exit(1)
if self.tc=='ISE':
self.recv_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/impl-ngd.log")
self.recv_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/impl-map.log")
self.recv_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/impl-par.log")
self.recv_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/{target}.map.v")
self.recv_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/{target}.map.sdf")
self.recv_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/{target}.ncd")
self.recv_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/{target}.pcf")
elif self.tc=="VIVADO":
self.recv_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/impl.log")
self.recv_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/post_impl.dcp")
def do_bit(self, target):
print("+ Generate output files")
self.send_file(self.configfile, 'project.cfg')
if self.tc=='ISE':
self.send_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/{target}.ncd")
self.send_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/{target}.pcf")
elif self.tc=="VIVADO":
self.send_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/post_impl.dcp")
self.heartbeat.printing = True
self.cmd(b'do'+self.sstr(f'bit {target}'))
res = struct.unpack('>I', self.channel.recv(4))[0]
self.heartbeat.printing = False
print(f' [{res}]')
if res != 0:
print("Some error occured...")
if self.tc=='ISE':
# TODO what to send?
pass
elif self.tc=="VIVADO":
self.recv_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/bit.log")
exit(1)
if self.tc=='ISE':
self.recv_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/timing.log")
self.recv_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/{target}.bit")
self.recv_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/{target}.bin")
elif self.tc=="VIVADO":
self.recv_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/bit.log")
self.recv_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/out.bit")
self.recv_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/out.bin")
self.recv_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/power.log")
self.recv_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/timing.log")
self.recv_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/util.log")
self.recv_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/system.xsa")
self.recv_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/total.dcp")
self.recv_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/impl_netlist.sdf")
self.recv_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/impl_netlist.v")
def do_floorplan(self, target):
print("Error: floorplan editing not implemented for remote execution")
exit(1)
def do_sim(self, target):
print("+ Simulate")
self.send_file(self.configfile, 'project.cfg')
src = self.config.get(target, 'src_vhdl', fallback='').split()
for s in src:
self.send_file(s)
src = self.config.get(target, 'src_verilog', fallback='').split()
for s in src:
self.send_file(s)
src = self.config.get(target, 'src_sysverilog', fallback='').split()
for s in src:
self.send_file(s)
src = self.config.get(target, 'src_sdf', fallback='')
if src!='':
self.send_file(src)
src = self.config.get(target, 'src_ip', fallback='').split()
for s in src:
if self.tc=='ISE':
self.send_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/{s}/{s}.vhd")
elif self.tc=="VIVADO":
self.send_file(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}/{s}/{s}.xci")
if self.tc=="VIVADO":
src = self.config.get(target, 'src_c', fallback='').split()
for s in src:
self.send_file(s)
self.heartbeat.printing = True
self.cmd(b'do'+self.sstr(f'sim {target}'))
res = struct.unpack('>I', self.channel.recv(4))[0]
self.heartbeat.printing = False
print(f' [{res}]')
self.recv_dir(f"{self.config.get('project', 'out_dir', fallback='OUT')}/{target}")

402
remotesyn/exec_VIVADO.py Normal file
View File

@ -0,0 +1,402 @@
import os
import shutil
import subprocess
import time
import glob
class exec_VIVADO:
def __init__(self, config, builddir):
self.config = config
self.builddir = builddir
self.create_builddir()
def create_outdir(self, target):
self.outdir = self.config.get('project', 'out_dir', fallback='OUT')
if not os.path.exists(self.outdir):
os.mkdir(self.outdir)
if not os.path.exists(f'{self.outdir}/{target}'):
os.mkdir(f'{self.outdir}/{target}')
def create_builddir(self):
if not os.path.exists(self.builddir):
os.mkdir(self.builddir)
def enter_builddir(self):
self.curdir = os.getcwd()
os.chdir(self.builddir)
def leave_builddir(self):
os.chdir(self.curdir)
def do_ip_gen(self, target):
# get used IP's for target
ips = self.config.get(target, 'src_ip', fallback='').split()
self.create_outdir(target)
print("+ Generate IPs")
dev = self.config.get('target', 'device', fallback='_d_')
sgrade = self.config.get('target', 'speedgrade', fallback='_s_')
pkg = self.config.get('target', 'package', fallback='_p_')
for i, ip in enumerate(ips):
self.enter_builddir()
ipsec = 'ip_%s'%ip
ipname = self.config.get(ipsec, ipsec)
ipconfig = '[ list \\\n'
for s in self.config[ipsec]:
if s==ipsec:
continue
ipconfig += f' CONFIG.{s.upper()} {{{self.config.get(ipsec, s)}}}\\\n'
ipconfig += ' ]'
with open('do.tcl', 'w') as f:
f.write(f"file mkdir {ip}\ncreate_project -in_memory\nset_property part {dev}{pkg}{sgrade} [current_project]\n")
f.write(f"create_ip -name {ipname.split(':')[2]} -vendor {ipname.split(':')[1]} -library {ipname.split(':')[0 ]} -module_name {ip} -dir {ip}\n")
f.write(f"set_property -dict {ipconfig} [ get_ips {ip} ]\n")
f.write(f"export_ip_user_files -of_objects [get_files {ip}/{ip}/{ip}.xci ] -no_script -sync -force -quiet\n")
f.write(f"upgrade_ip [get_ips]\ngenerate_target all [get_ips]\n#synth_ip [get_ips]\n")
# f.write(f"export_simulation -directory {ip} -simulator xsim -absolute_path -export_source_files -of_objects [get_files {ip}/{ip}/{ip}.xci]")
pid = subprocess.Popen('vivado -mode batch -source do.tcl', shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
while pid.poll() is None:
print('.', end='', flush=True)
time.sleep(2)
ret = pid.returncode
print('')
if ret != 0:
self.leave_builddir()
if not os.path.exists(f'{self.outdir}/{target}/{ip}'):
os.mkdir(f'{self.outdir}/{target}/{ip}')
shutil.copyfile(f"{self.builddir}/vivado.log", f'{self.outdir}/{target}/{ip}/log.log')
exit(ret)
self.leave_builddir()
if not os.path.exists(f'{self.outdir}/{target}/{ip}'):
os.mkdir(f'{self.outdir}/{target}/{ip}')
shutil.copyfile(f"{self.builddir}/{ip}/{ip}/{ip}.vho", f'{self.outdir}/{target}/{ip}/{ip}.vho')
shutil.copyfile(f"{self.builddir}/{ip}/{ip}/{ip}.veo", f'{self.outdir}/{target}/{ip}/{ip}.veo')
shutil.copyfile(f"{self.builddir}/{ip}/{ip}/{ip}.xci", f'{self.outdir}/{target}/{ip}/{ip}.xci')
shutil.copyfile(f"{self.builddir}/vivado.log", f'{self.outdir}/{target}/{ip}/log.log')
for f in glob.glob(f"{self.builddir}/{ip}/{ip}/*.c"):
shutil.copy(f, f'{self.outdir}/{target}/{ip}/')
for f in glob.glob(f"{self.builddir}/{ip}/{ip}/*.h"):
shutil.copy(f, f'{self.outdir}/{target}/{ip}/')
def do_synthesize(self, target):
self.create_outdir(target)
self.enter_builddir()
print('+ Synthesize')
extra_opts = self.config.get(target, 'extra_options').split('\n')
opts = {}
for o in extra_opts:
tp = o.split()[0]
op = ' '.join(o.split()[1:])
opts[tp] = op
if 'syn' not in opts:
opts['syn'] = ''
if 'netlist_top' not in opts:
opts['netlist_top'] = self.config.get(target, 'toplevel', fallback='toplevel')
dev = self.config.get('target', 'device', fallback='_d_')
sgrade = self.config.get('target', 'speedgrade', fallback='_s_')
pkg = self.config.get('target', 'package', fallback='_p_')
with open('do.tcl', 'w') as f:
src = self.config.get(target, 'src_vhdl', fallback='').split()
for s in src:
f.write(f'read_vhdl "{self.curdir}/{s}"\n')
src = self.config.get(target, 'src_verilog', fallback='').split()
for s in src:
f.write(f'read_verilog "{self.curdir}/{s}"\n')
src = self.config.get(target, 'src_sysverilog', fallback='').split()
for s in src:
f.write(f'read_verilog -sv "{self.curdir}/{s}"\n')
src = self.config.get(target, 'src_constraints', fallback='')
f.write(f'read_xdc "{self.curdir}/{src}"\n')
src = self.config.get(target, 'src_ip', fallback='').split()
for s in src:
if os.path.exists(s):
shutil.rmtree(s)
os.mkdir(s)
shutil.copyfile(f'{self.curdir}/{self.outdir}/{target}/{s}/{s}.xci', f'{s}/{s}.xci')
f.write(f'read_ip "{s}/{s}.xci"\n')
f.write(f"set_property part {dev}{pkg}{sgrade} [current_project]\n")
f.write(f"upgrade_ip [get_ips]\ngenerate_target all [get_ips]\nsynth_ip [get_ips]\n")
f.write(f"synth_design -top {self.config.get(target, 'toplevel', fallback='toplevel')} -part {dev}{pkg}{sgrade} {opts['syn']}\n")
f.write(f"write_checkpoint -force post_synth.dcp\nwrite_verilog -force -mode timesim -cell {opts['netlist_top']} -sdf_anno true -nolib netlist.v\n")
f.write(f"write_sdf -force -cell {opts['netlist_top']} -mode timesim netlist.sdf\n")
pid = subprocess.Popen('vivado -mode batch -source do.tcl', shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
while pid.poll() is None:
print('.', end='', flush=True)
time.sleep(2)
ret = pid.returncode
print('')
if ret != 0:
self.leave_builddir()
shutil.copyfile(f"{self.builddir}/vivado.log", f'{self.outdir}/{target}/synth.log')
exit(ret)
self.leave_builddir()
shutil.copyfile(f'{self.builddir}/netlist.v', f'{self.outdir}/{target}/synth_netlist.v')
shutil.copyfile(f'{self.builddir}/netlist.sdf', f'{self.outdir}/{target}/synth_netlist.sdf')
shutil.copyfile(f'{self.builddir}/post_synth.dcp', f'{self.outdir}/{target}/post_synth.dcp')
shutil.copyfile(f"{self.builddir}/vivado.log", f'{self.outdir}/{target}/synth.log')
def do_implement(self, target):
self.create_outdir(target)
self.enter_builddir()
print('+ Implement')
extra_opts = self.config.get(target, 'extra_options').split('\n')
opts = {}
for o in extra_opts:
tp = o.split()[0]
op = ' '.join(o.split()[1:])
opts[tp] = op
if 'opt' not in opts:
opts['opt'] = ''
if 'place' not in opts:
opts['place'] = ''
if 'route' not in opts:
opts['route'] = ''
with open('do.tcl', 'w') as f:
f.write(f"open_checkpoint {self.curdir}/{self.outdir}/{target}/post_synth.dcp\n")
f.write(f"opt_design {opts['opt']}\nplace_design {opts['place']}\nroute_design {opts['route']}\n")
f.write(f"write_checkpoint -force {self.curdir}/{self.outdir}/{target}/post_impl.dcp\n")
pid = subprocess.Popen('vivado -mode batch -source do.tcl', shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
while pid.poll() is None:
print('.', end='', flush=True)
time.sleep(2)
ret = pid.returncode
print('')
if ret != 0:
self.leave_builddir()
shutil.copyfile(f"{self.builddir}/vivado.log", f'{self.outdir}/{target}/impl.log')
exit(ret)
self.leave_builddir()
shutil.copyfile(f"{self.builddir}/vivado.log", f'{self.outdir}/{target}/impl.log')
def do_bit(self, target):
self.create_outdir(target)
self.enter_builddir()
print('+ Generate output files')
extra_opts = self.config.get(target, 'extra_options').split('\n')
opts = {}
for o in extra_opts:
tp = o.split()[0]
op = ' '.join(o.split()[1:])
opts[tp] = op
if 'netlist_top' not in opts:
opts['netlist_top'] = self.config.get(target, 'toplevel', fallback='toplevel')
with open('do.tcl', 'w') as f:
f.write(f"open_checkpoint {self.curdir}/{self.outdir}/{target}/post_impl.dcp\n")
f.write(f"set_property SEVERITY {{Warning}} [get_drc_checks NSTD-1]\nset_property SEVERITY {{Warning}} [get_drc_checks UCIO-1]\n")
f.write(f"set_property BITSTREAM.General.UnconstrainedPins {{Allow}} [current_design]\n")
f.write(f"write_debug_probes -force out.ltx\nwrite_bitstream -force -bin_file out.bit\nreport_timing_summary -file timing.log\nreport_power -file power.log\n")
f.write(f"report_utilization -file util.log\n")
f.write(f"write_checkpoint -force {self.curdir}/{self.outdir}/{target}/total.dcp\n")
f.write(f"open_checkpoint {self.curdir}/{self.outdir}/{target}/total.dcp\n")
f.write(f"write_hw_platform -fixed -force -file {self.curdir}/{self.outdir}/{target}/system.xsa\n")
f.write(f"write_verilog -force -mode timesim -cell {opts['netlist_top']} -rename_top {opts['netlist_top']} -sdf_anno true netlist.v\n") # -nolib
f.write(f"write_sdf -force -cell {opts['netlist_top']} -rename_top {opts['netlist_top']} -mode timesim netlist.sdf\n")
pid = subprocess.Popen('vivado -mode batch -source do.tcl', shell=True, stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
while pid.poll() is None:
print('.', end='', flush=True)
time.sleep(2)
ret = pid.returncode
print('')
if ret != 0:
self.leave_builddir()
shutil.copyfile(f"{self.builddir}/vivado.log", f'{self.outdir}/{target}/bit.log')
exit(ret)
self.leave_builddir()
shutil.copyfile(f"{self.builddir}/vivado.log", f'{self.outdir}/{target}/bit.log')
shutil.copyfile(f"{self.builddir}/timing.log", f'{self.outdir}/{target}/timing.log')
shutil.copyfile(f"{self.builddir}/util.log", f'{self.outdir}/{target}/util.log')
shutil.copyfile(f"{self.builddir}/power.log", f'{self.outdir}/{target}/power.log')
shutil.copyfile(f"{self.builddir}/out.bit", f'{self.outdir}/{target}/out.bit')
shutil.copyfile(f"{self.builddir}/out.bin", f'{self.outdir}/{target}/out.bin')
shutil.copyfile(f"{self.builddir}/netlist.v", f'{self.outdir}/{target}/impl_netlist.v')
shutil.copyfile(f"{self.builddir}/netlist.sdf", f'{self.outdir}/{target}/impl_netlist.sdf')
def do_floorplan(self, target):
self.create_outdir(target)
self.enter_builddir()
print('+ Open floorplan viewer')
with open('do.tcl', 'w') as f:
f.write(f"open_checkpoint {self.curdir}/{self.outdir}/{target}/post_impl.dcp\n")
f.write(f"start_gui")
pid = subprocess.Popen('vivado -mode batch -source do.tcl', shell=True, stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
while pid.poll() is None:
print('.', end='', flush=True)
time.sleep(2)
ret = pid.returncode
print('')
self.leave_builddir()
if ret != 0:
exit(ret)
def do_sim(self, target):
self.create_outdir(target)
self.enter_builddir()
print('+ Simulate')
if os.path.exists('sim'):
shutil.rmtree('sim')
dev = self.config.get('target', 'device', fallback='_d_')
sgrade = self.config.get('target', 'speedgrade', fallback='_s_')
pkg = self.config.get('target', 'package', fallback='_p_')
with open('do.tcl', 'w') as f:
f.write(f"create_project -force -part {dev}{pkg}{sgrade} sim sim\n")
src = self.config.get(target, 'src_vhdl', fallback='').split()
for s in src:
f.write(f'add_files -norecurse -scan_for_includes {self.curdir}/{s}\n')
f.write(f'import_files -norecurse {self.curdir}/{s}\n')
src = self.config.get(target, 'src_verilog', fallback='').split()
for s in src:
f.write(f'add_files -norecurse -scan_for_includes {self.curdir}/{s}\n')
f.write(f'import_files -norecurse {self.curdir}/{s}\n')
src = self.config.get(target, 'src_sysverilog', fallback='').split()
for s in src:
f.write(f'add_files -norecurse -scan_for_includes {self.curdir}/{s}\n')
f.write(f'import_files -norecurse {self.curdir}/{s}\n')
src = self.config.get(target, 'src_ip', fallback='').split()
for s in src:
f.write(f'add_files -norecurse {self.curdir}/{self.outdir}/{s}/{s}.xci\n')
src = self.config.get(target, 'src_c', fallback='').split()
for s in src:
if s.endswith('.h'):
continue
f.write(f'add_files -norecurse -scan_for_includes {self.curdir}/{s}\n')
f.write(f'import_files -norecurse {self.curdir}/{s}\n')
if self.config.get(target, 'src_sdf', fallback='__sdf__') != '__sdf__':
s = self.config.get(target, 'src_sdf', fallback='__sdf__')
f.write(f'add_files -norecurse -scan_for_includes {self.curdir}/{s}\n')
f.write(f'import_files -norecurse {self.curdir}/{s}\n')
f.write(f"file mkdir sim/sim.sim/sim_1/behav/xsim\nfile copy -force {self.curdir}/{s} {self.curdir}/{self.builddir}/sim/sim.sim/sim_1/behav/xsim/netlist.sdf\n")
# f.write(f"file mkdir sim/sim.sim/sim_1/behav/xsim\nfile copy -force {self.curdir}/{s} {self.curdir}/{self.builddir}/sim/sim.sim/sim_1/behav/xsim/{os.path.split(s)[1]}\n")
f.write(f"set_property top {self.config.get(target, 'toplevel', fallback='toplevel')} [get_filesets sim_1]\n")
f.write("set_property top_lib xil_defaultlib [get_filesets sim_1]\n")
f.write("launch_simulation -noclean_dir -scripts_only -absolute_path\n")
pid = subprocess.Popen('vivado -mode batch -source do.tcl', shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
while pid.poll() is None:
print('.', end='', flush=True)
time.sleep(2)
ret = pid.returncode
print('')
if ret != 0:
self.leave_builddir()
print("Something went wrong...")
shutil.copyfile(f"{self.builddir}/vivado.log", f'{self.outdir}/{target}/prepare.log')
exit(ret)
shutil.copyfile(f"vivado.log", f'{self.curdir}/{self.outdir}/{target}/prepare.log')
extras = ''
if self.config.get(target, 'simtype', fallback='presim') == 'postsim':
extras = f"-{self.config.get(target, 'delay', fallback='typ')}delay -transport_int_delays -pulse_r 0 -pulse_int_r 0 -L simprims_ver"
pid = subprocess.Popen(f'sed -i "s/xelab/xelab {extras}/g" elaborate.sh', shell=True, cwd='sim/sim.sim/sim_1/behav/xsim', stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
while pid.poll() is None:
print('.', end='', flush=True)
time.sleep(2)
ret = pid.returncode
print('')
if ret!=0:
print("Something went wrong with editing elaborate stage...")
exit(ret)
if self.config.get(target, 'simtype', fallback='presim') == 'postsim':
pid = subprocess.Popen(f"sed -i '/ \/I /d' netlist.sdf && sed -i '/glbl.v/d' *.prj", shell=True, cwd='sim/sim.sim/sim_1/behav/xsim', stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
# pid = subprocess.Popen(f"sed -i '/glbl.v/d' *.prj", shell=True, cwd='sim/sim.sim/sim_1/behav/xsim', stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
while pid.poll() is None:
print('.', end='', flush=True)
time.sleep(2)
ret = pid.returncode
print('')
if ret!=0:
print("Something went wrong with editing project files...")
exit(ret)
pid = subprocess.Popen(f'bash compile.sh', shell=True, cwd='sim/sim.sim/sim_1/behav/xsim', stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
while pid.poll() is None:
print('.', end='', flush=True)
time.sleep(2)
ret = pid.returncode
print('')
if ret!=0:
self.leave_builddir()
print("Compile error")
shutil.copyfile(f"{self.builddir}/sim/sim.sim/sim_1/behav/xsim/compile.log", f'{self.outdir}/{target}/compile.log')
exit(ret)
shutil.copyfile(f"{self.curdir}/{self.builddir}/sim/sim.sim/sim_1/behav/xsim/compile.log", f'{self.curdir}/{self.outdir}/{target}/compile.log')
pid = subprocess.Popen(f'bash elaborate.sh', shell=True, cwd='sim/sim.sim/sim_1/behav/xsim', stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
while pid.poll() is None:
print('.', end='', flush=True)
time.sleep(2)
ret = pid.returncode
print('')
if ret!=0:
self.leave_builddir()
print("Elaborate error")
shutil.copyfile(f"{self.builddir}/sim/sim.sim/sim_1/behav/xsim/elaborate.log", f'{self.outdir}/{target}/elaborate.log')
exit(ret)
shutil.copyfile(f"{self.curdir}/{self.builddir}/sim/sim.sim/sim_1/behav/xsim/elaborate.log", f'{self.curdir}/{self.outdir}/{target}/elaborate.log')
with open(f"sim/sim.sim/sim_1/behav/xsim/{self.config.get(target, 'toplevel', fallback='toplevel')}.tcl", 'w') as f:
f.write(f"open_vcd out.vcd\nlog_vcd\nrun {self.config.get(target, 'runtime', fallback='100 ns')}\nclose_vcd\nquit\n")
pid = subprocess.Popen(f'bash simulate.sh', shell=True, cwd='sim/sim.sim/sim_1/behav/xsim', stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
while pid.poll() is None:
print('.', end='', flush=True)
time.sleep(2)
ret = pid.returncode
print('')
if ret!=0:
self.leave_builddir()
print("Simulation error")
shutil.copyfile(f"{self.builddir}/sim/sim.sim/sim_1/behav/xsim/simulate.log", f'{self.outdir}/{target}/simulate.log')
exit(ret)
shutil.copyfile(f"{self.curdir}/{self.builddir}/sim/sim.sim/sim_1/behav/xsim/simulate.log", f'{self.curdir}/{self.outdir}/{target}/simulate.log')
self.leave_builddir()
shutil.copyfile(f'{self.builddir}/sim/sim.sim/sim_1/behav/xsim/out.vcd', f'{self.outdir}/{target}/output.vcd')

1
requirements.txt Normal file
View File

@ -0,0 +1 @@
paramiko

233
scripts/remotesyn Normal file
View File

@ -0,0 +1,233 @@
#!/usr/bin/env python3
import sys
import os
import configparser
import paramiko
import socket
from remotesyn import Connection, exec_ISE, exec_VIVADO, exec_REMOTE
def server(accidentals, positionals):
addr = accidentals['server'].split(':')
if len(addr)!=2:
print("Host address must be in form hostname:port")
exit(1)
host_key = paramiko.RSAKey(filename=accidentals['privkey'])
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((addr[0], int(addr[1])))
sock.listen(100)
# Get authorized hosts
with open(accidentals['authorized'], 'r') as f:
authorized = f.read().split('\n')
running = True
threads = []
try:
while running:
client, addr = sock.accept()
conn = Connection(client, addr, host_key, authorized)
conn.start()
threads.append(conn)
except KeyboardInterrupt:
print("Stopping server")
running = False
for t in threads:
t.stop()
finally:
sock.close()
def main(accidentals, positionals):
if 'server' in accidentals:
return server(accidentals, positionals)
elif 'local' in accidentals:
config = accidentals['config']
if config.get('project', 'toolchain') == 'ISE':
execInterface = exec_ISE(config, accidentals['builddir'])
else:
execInterface = exec_VIVADO(config, accidentals['builddir'])
else:
config = accidentals['config']
execInterface = exec_REMOTE(config, accidentals['configname'])
i = 0
while i< len(positionals):
action = positionals[i]
i += 1
if action == 'ip':
if i>=len(positionals):
print("Unexpected end of input")
exit(1)
target = positionals[i]
i += 1
execInterface.do_ip_gen(target)
elif action == 'syn':
if i>=len(positionals):
print("Unexpected end of input")
exit(1)
target = positionals[i]
i += 1
execInterface.do_synthesize(target)
elif action == 'impl':
if i>=len(positionals):
print("Unexpected end of input")
exit(1)
target = positionals[i]
i += 1
execInterface.do_implement(target)
elif action == 'bit':
if i>=len(positionals):
print("Unexpected end of input")
exit(1)
target = positionals[i]
i += 1
execInterface.do_bit(target)
elif action == 'all':
if i>=len(positionals):
print("Unexpected end of input")
exit(1)
target = positionals[i]
i += 1
execInterface.do_synthesize(target)
execInterface.do_implement(target)
execInterface.do_bit(target)
elif action == 'floorplan':
if i>=len(positionals):
print("Unexpected end of input")
exit(1)
target = positionals[i]
i += 1
execInterface.do_floorplan(target)
elif action == 'sim':
if i>=len(positionals):
print("Unexpected end of input")
exit(1)
target = positionals[i]
i += 1
execInterface.do_sim(target)
else:
print("Unknown action...")
exit(1)
def print_help():
print("Unified FPGA synthesizer frontend\r\n(c) Joppe Blondel - 2022\r\n")
print(f"Usage: {sys.argv[0]} [ OPTIONS ] action [ target ] ...")
print("where OPTIONS := { -h | -l | -s host:port privkey pubkey authorized | -c config | -b build_dir }")
print(" action := { ip | syn | impl | bit | all | floorplan | sim | init }")
print("")
print("Options:")
print(" -h Show this help message")
print(" -l Local build")
print(" -s <s-info> Start server with server information s-info. host:port is the address to bind")
print(" to, privkey and pubkey are the ssh keys of the server and authorized is the")
print(" authorized_keys file for the SSH server")
print(" -c <file> Configuration file, defaults to project.cfg")
print(" -b <dir> Build directory, defaults to .build")
print("")
print("Actions:")
print("ip <target> Generate IP files from vendor provided libraries")
print("syn <target> Synthesize design for target")
print("impl <target> Route and place design for target")
print("bit <target> Generate output files and run analysis for target")
print("all <target> Generate IP, synthesize, route and place design for target")
print("floorplan <target> Run floorplan editor, currently only for local execution")
print("sim <simtarget> Run simulation target")
print("init Initialize project")
if __name__=="__main__":
accidentals = {}
positionals = []
if len(sys.argv)==1:
print_help()
exit(1)
i = 1
while i<len(sys.argv):
if sys.argv[i].startswith('-'):
if sys.argv[i] == '-h':
print_help()
exit(0)
elif sys.argv[i] == '-l':
accidentals['local'] = True
elif sys.argv[i] == '-s':
if i+1>=len(sys.argv):
print("Unexpected end of input")
exit(1)
accidentals['server'] = sys.argv[i+1]
i += 1
if i+1>=len(sys.argv):
print("Unexpected end of input")
exit(1)
accidentals['privkey'] = sys.argv[i+1]
i += 1
if i+1>=len(sys.argv):
print("Unexpected end of input")
exit(1)
accidentals['pubkey'] = sys.argv[i+1]
i += 1
if i+1>=len(sys.argv):
print("Unexpected end of input")
exit(1)
accidentals['authorized'] = sys.argv[i+1]
i += 1
elif sys.argv[i] == '-c':
if i+1>=len(sys.argv):
print("Unexpected end of input")
exit(1)
accidentals['config'] = sys.argv[i+1]
i += 1
elif sys.argv[i] == '-b':
if i+1>=len(sys.argv):
print("Unexpected end of input")
exit(1)
accidentals['builddir'] = sys.argv[i+1]
i += 1
else:
if sys.argv[i] == 'init':
print("+ Generate example configuration file")
with open('example.cfg', 'w') as f:
f.write("# PROJECT SETTINGS\n")
f.write("# ----------------\n")
f.write("[server]\nhostname = localhost\nport = 8080\nprivkey = keys/id_rsa\npubkey = keys/id_rsa.pub\n\n")
f.write("[project]\n# Toolchain selection. choose between [ISE, VIVADO]\ntoolchain = ISE\nout_dir = OUT\n\n")
f.write("[target]\nfamily = spartan6\ndevice = xc6lsx9\npackage = tgq144\nspeedgrade = -2\n\n")
f.write("# HARDWARE TARGETS\n")
f.write("# ----------------\n")
f.write("src_vhdl = RTL/toplevel.vhd\nsrc_verilog = \nsrc_sysverilog = \n")
f.write("src_constraints = CON/toplevel.ucf\nsrc_ip = \ntoplevel = toplevel\nextra_options = ")
exit(1)
positionals.append(sys.argv[i])
i += 1
if 'config' not in accidentals and 'server' not in accidentals:
# Running in client mode and no config file specified -> pick default
accidentals['config'] = 'project.cfg'
if 'config' in accidentals and 'server' not in accidentals:
if os.path.exists(accidentals['config']):
accidentals['configname'] = accidentals['config']
config = configparser.ConfigParser()
config.read(accidentals['config'])
accidentals['config'] = config
else:
print(f"Config file \'{accidentals['config']}\' not found")
exit(1)
if ('server' in accidentals or 'local' in accidentals) and 'builddir' not in accidentals:
accidentals['builddir'] = '.build'
main(accidentals, positionals)
exit(0)

2
setup.cfg Normal file
View File

@ -0,0 +1,2 @@
[metadata]
description-file = README.md

27
setup.py Normal file
View File

@ -0,0 +1,27 @@
from setuptools import setup
with open("README.md", 'r') as f:
long_description = f.read()
setup(
name='remotesyn',
version='0.1',
description='Remote FPGA synthesis abstraction tool',
author='Joppe Blondel',
author_email='joppe@blondel.nl',
download_url='',
url='https://git.joppeb.nl/joppe/remotesyn',
keywords = ['FPGA', 'Synthesis', 'Xilinx', 'ISE', 'Vivado',],
classifiers=[
'Development Status :: 3 - Alpha',
'License :: OSI Approved :: BSD License',
'Programming Language :: Python :: 3',
],
packages=['remotesyn'],
licence='BSD Licence',
install_requires=['paramiko'],
scripts=['scripts/remotesyn']
)