| import os | |
| import shutil | |
| import sys | |
| import subprocess | |
| import signal | |
| from multiprocessing import Pool, Lock | |
| from itertools import chain | |
| import argparse | |
| # Can be used to redirect vivado tons of output | |
| # stdout=DEVNULL in subprocess.check_call | |
| # Worker function called from threads. | |
| # Once the worker completes the job, the temporary files | |
| # get merged with the final outputs in a thread-safe way | |
| # and deleted to save disk usage. | |
| # To do so, a global Lock is provided at the Pool initialization. | |
| def start_pips(argList): | |
| blockID, start, stop, total = argList | |
| print("Running instance :" + str(blockID) + " / " + str(total)) | |
| subprocess.check_call( | |
| "${URAY_VIVADO} -mode batch -source $FUZDIR/job.tcl -tclargs " + | |
| str(blockID) + " " + str(start) + " " + str(stop), | |
| shell=True) | |
| uphill_wires = "wires/uphill_wires_{}.txt".format(blockID) | |
| downhill_wires = "wires/downhill_wires_{}.txt".format(blockID) | |
| # Locking to write on final file and remove the temporary one | |
| Lock.acquire() | |
| with open("uphill_wires.txt", "a") as wfd: | |
| f = uphill_wires | |
| with open(f, "r") as fd: | |
| shutil.copyfileobj(fd, wfd) | |
| with open("downhill_wires.txt", "a") as wfd: | |
| f = downhill_wires | |
| with open(f, "r") as fd: | |
| shutil.copyfileobj(fd, wfd) | |
| Lock.release() | |
| os.remove(uphill_wires) | |
| os.remove(downhill_wires) | |
| # Function called once to get the total numbers of pips to list | |
| def get_nb_pips(): | |
| print("Fetching total number of pips") | |
| subprocess.check_call( | |
| "${URAY_VIVADO} -mode batch -source $FUZDIR/get_pipscount.tcl", | |
| shell=True) | |
| countfile = open("nb_pips.txt", "r") | |
| return int(countfile.readline()) | |
| def pool_init(lock): | |
| global Lock | |
| Lock = lock | |
| def run_pool(itemcount, nbBlocks, blocksize, nbParBlock, workFunc): | |
| # We handle the case of not integer multiple of pips | |
| intitemcount = blocksize * nbBlocks | |
| lastRun = False | |
| modBlocks = itemcount - intitemcount | |
| if modBlocks != 0: | |
| lastRun = True | |
| nbBlocks = nbBlocks + 1 | |
| print("Items Count: " + str(itemcount) + " - Number of blocks: " + | |
| str(nbBlocks) + " - Parallel blocks: " + str(nbParBlock) + | |
| " - Blocksize: " + str(blocksize) + " - Modulo Blocks: " + | |
| str(modBlocks)) | |
| blockId = range(0, nbBlocks) | |
| startI = range(0, intitemcount, blocksize) | |
| stopI = range(blocksize, intitemcount + 1, blocksize) | |
| totalBlock = [nbBlocks for _ in range(nbBlocks)] | |
| # In case we have a last incomplete block we add it as a last | |
| # element in the arguments list | |
| if lastRun == True: | |
| startI = chain(startI, [intitemcount]) | |
| stopI = chain(stopI, [itemcount]) | |
| mpLock = Lock() | |
| argList = zip(blockId, startI, stopI, totalBlock) | |
| with Pool( | |
| processes=nbParBlock, initializer=pool_init, | |
| initargs=(mpLock, )) as pool: | |
| pool.map(workFunc, argList) | |
| return nbBlocks | |
| # ========================================================================== | |
| # ===== FPGA Logic Items data ============================================== | |
| # For Artix 7 50T: | |
| # - Total pips: 22002368 | |
| # - Total tiles: 18055 | |
| # - Total nodes: 1953452 | |
| # For Kintex 7 70T: | |
| # - Total pips: 29424910 | |
| # - Total tiles: 24453 | |
| # - Total nodes: 2663055 | |
| # For Zynq 7 z010: | |
| # - Total pips: 12462138 | |
| # - Total tiles: 13440 | |
| # - Total nodes: 1122477 | |
| # ========================================================================= | |
| # Dividing by about 64 over 4 core is not optimized but a default to run | |
| # on most computer | |
| # ========================================================================= | |
| def main(argv): | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument( | |
| "-p", | |
| "--nbPar", | |
| help="Number of parallel instances of Vivado", | |
| type=int, | |
| default=4) | |
| parser.add_argument( | |
| "-t", | |
| "--sizePipsBlock", | |
| help="Define the number of pips to process per instance", | |
| type=int, | |
| default=340000) | |
| args = parser.parse_args() | |
| nbParBlock = args.nbPar | |
| blockPipsSize = args.sizePipsBlock | |
| pipscount = get_nb_pips() | |
| nbPipsBlock = int(pipscount / blockPipsSize) | |
| if not os.path.exists("wires"): | |
| os.mkdir("wires") | |
| print(" nbPar: " + str(nbParBlock) + " blockPipsSize: " + | |
| str(blockPipsSize)) | |
| pipsFileCount = run_pool(pipscount, nbPipsBlock, blockPipsSize, nbParBlock, | |
| start_pips) | |
| print("Work done !") | |
| return 0 | |
| if __name__ == "__main__": | |
| sys.exit(main(sys.argv)) |