Skip to content

Instantly share code, notes, and snippets.

@carlosedp
Last active February 17, 2022 21:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save carlosedp/a7b4386b41ffdf60873c05eeae7155ec to your computer and use it in GitHub Desktop.
Save carlosedp/a7b4386b41ffdf60873c05eeae7155ec to your computer and use it in GitHub Desktop.
Project X-Ray Edalize Backend and Launcher for Docker Containers

Kintex 7 PrjXRay

Testing out the PrjXRay backend for the Kintex 7 FPGA

  1. Grab the Dockerfile above and build the container image with docker build -t carlosedp/prjxray:kintex7 -f Dockerfile .. If you are on a Linux box with more than 10GB of RAM, check the Dockerfile to use it to build the database otherwise, built externally and leave the "ADD" command.
  2. Save the edalize_launcher.py script somewhere in your machine
  3. Install FuseSoc with pip3 install --upgrade --user fusesoc
  4. Check-out my fork of Edalize (the FuseSoc backend with git clone -b xray https://github.com/carlosedp/edalize
  5. Install edalize locally (it will replace your local install if you already have it) with cd edalize && pip install -e .
  6. Grab a sample app from git clone -b fusesoc https://github.com/carlosedp/xc7k325t-blinky-nextpnr.git
  7. Run it using Fusesoc, the launcher for containers and the image with EDALIZE_LAUNCHER=$(realpath ../edalize_launcher.py) fusesoc run --target=qmtech_k325t xc7k325t:test:blinky
ARG PART = xc7k325tffg676-1
# Set this to the part you want to build for
# pass it thru the command line
# e.g. --build-arg PART=xc7k325tffg676-1
FROM python:3.5 as builder
RUN apt-get update && \
apt-get upgrade -y && \
apt-get install -y sudo ca-certificates git build-essential wget cmake xz-utils python3-yaml libboost-all-dev qt5-default libqt5opengl5-dev libeigen3-dev --no-install-recommends
ENV PART xc7k325tffg676-1
# Build NextPNR
RUN git clone -b xilinx-upstream --recurse-submodules https://github.com/kintex-chatter/nextpnr-xilinx.git
RUN cd nextpnr-xilinx && \
mkdir build && \
cd build && \
cmake -DARCH=xilinx -DCMAKE_INSTALL_PREFIX=/opt/nextpnr .. && \
make -j$(nproc) && make install && \
cd ..
# Build chipdb
# This process requires about 10GB of RAM
# If you run Docker in a VM (e.g. MacOS) you might need to build it outside the VM or increase RAM.
RUN cd xilinx/external && \
rm -rf prjxray-db && \
git clone -b k325 https://github.com/kintex-chatter/prjxray-db.git && \
cd ../.. && \
python3 xilinx/python/bbaexport.py --device ${PART} --bba xilinx/${PART}.bba && \
build/bbasm -l xilinx/${PART}.bba xilinx/${PART}.bin && \
mkdir -p /opt/nextpnr/xilinx-chipdb
FROM python:3.5 as final
RUN apt-get update && \
apt-get upgrade -y && \
apt-get install -y ca-certificates git curl wget xz-utils libboost-all-dev libqt5opengl5-dev --no-install-recommends
COPY --from=builder /opt/nextpnr /opt/nextpnr
COPY --from=builder /xilinx/external/prjxray-db /opt/nextpnr/prjxray-db
# The database build process required almost 10GB RAM and can be built outside the container environment.
# If used container to build, uncomment next line and comment the ADD below it.
# COPY --from=builder /root/nextpnr-xilinx/xilinx/xc7k325tffg676-1.bin /opt/nextpnr/xilinx-chipdb/
ADD ${PART}.bin /opt/nextpnr/xilinx-chipdb/
RUN useradd -m fpga && echo "fpga:fpga" | chpasswd && \
adduser fpga sudo && \
apt-get install -y python3-yaml cmake build-essential && \
mkdir -p /opt/prjxray && \
chmod 777 /opt/prjxray
# Build and install Prjxray
RUN git clone --recurse-submodules https://github.com/SymbiFlow/prjxray /opt/prjxray && \
cd /opt/prjxray && \
pip3 install -r requirements.txt && \
cd /opt/nextpnr && \
git clone -b k325 --recurse-submodules https://github.com/kintex-chatter/prjxray-db.git
USER fpga
RUN cd /opt/prjxray && \
mkdir build && \
cd build && \
cmake -DCMAKE_INSTALL_PREFIX=/opt/prjxray .. && \
make -j$(nproc)
USER root
RUN cd /opt/prjxray/build && \
make install
ENV PATH /opt/nextpnr/bin:/opt/prjxray/bin:$PATH
ENV CHIPDB /opt/nextpnr/xilinx-chipdb
ENV DB_DIR /opt/nextpnr/prjxray-db
CMD ["bash"]
#!/usr/bin/python3
import os
import shutil
import subprocess
import sys
import logging
import shlex
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger("EdalizeLauncher")
symbiflow_init = "bash -lec {}"
prjxray_init = "bash -c {}"
# fmt: off
containers = [
{'tool': 'ghdl', 'image': 'ghdl/ghdl:buster-llvm-7', 'init': '', 'vendor': '', 'part': ''},
{'tool': 'verilator', 'image': 'verilator/verilator', 'init': '', 'vendor': '', 'part': ''},
{'tool': 'flow.tcl', 'image': 'edalize/openlane-sky130:v0.12', 'init': '', 'vendor': '', 'part': ''},
{'tool': 'mill', 'image': 'adoptopenjdk:8u282-b08-jre-hotspot', 'init': '', 'vendor': '', 'part': ''},
{'tool': 'sbt', 'image': 'adoptopenjdk:8u282-b08-jre-hotspot', 'init': '', 'vendor': '', 'part': ''},
{'tool': 'yosys', 'image': 'hdlc/yosys', 'init': '', 'vendor': '', 'part': ''},
{'tool': 'nextpnr-ice40', 'image': 'hdlc/nextpnr', 'init': '', 'vendor': '', 'part': ''},
{'tool': 'nextpnr-ecp5', 'image': 'hdlc/nextpnr', 'init': '', 'vendor': '', 'part': ''},
{'tool': 'nextpnr-nexus', 'image': 'hdlc/nextpnr', 'init': '', 'vendor': '', 'part': ''},
{'tool': 'icepack', 'image': 'hdlc/icestorm', 'init': '', 'vendor': '', 'part': ''},
{'tool': 'ecppack', 'image': 'hdlc/prjtrellis', 'init': '', 'vendor': '', 'part': ''},
{'tool': 'icetime', 'image': 'hdlc/icestorm', 'init': '', 'vendor': '', 'part': ''},
{'tool': 'icebox_stat', 'image': 'hdlc/icestorm', 'init': '', 'vendor': '', 'part': ''},
{'tool': 'icepll', 'image': 'hdlc/icestorm', 'init': '', 'vendor': '', 'part': ''},
{'tool': 'ecppll', 'image': 'hdlc/prjtrellis', 'init': '', 'vendor': '', 'part': ''},
{'tool': 'nextpnr-xilinx', 'image': 'carlosedp/prjxray:kintex7', 'init': prjxray_init, 'vendor': '', 'part': ''},
{'tool': 'fasm2frames', 'image': 'carlosedp/prjxray:kintex7', 'init': prjxray_init, 'vendor': '', 'part': ''},
{'tool': 'xc7frames2bit', 'image': 'carlosedp/prjxray:kintex7', 'init': prjxray_init, 'vendor': '', 'part': ''},
# For Quicklogic EOS S3
{'tool': 'ql_symbiflow', 'image': 'gcr.io/hdl-containers/conda/symbiflow/eos-s3', 'init': symbiflow_init, 'vendor': 'quicklogic', 'part': 'ql-eos-s3'},
{'tool': 'symbiflow_synth', 'image': 'gcr.io/hdl-containers/conda/symbiflow/eos-s3', 'init': symbiflow_init, 'vendor': 'quicklogic', 'part': 'ql-eos-s3'},
{'tool': 'symbiflow_pack', 'image': 'gcr.io/hdl-containers/conda/symbiflow/eos-s3', 'init': symbiflow_init, 'vendor': 'quicklogic', 'part': 'ql-eos-s3'},
{'tool': 'symbiflow_place', 'image': 'gcr.io/hdl-containers/conda/symbiflow/eos-s3', 'init': symbiflow_init, 'vendor': 'quicklogic', 'part': 'ql-eos-s3'},
{'tool': 'symbiflow_route', 'image': 'gcr.io/hdl-containers/conda/symbiflow/eos-s3', 'init': symbiflow_init, 'vendor': 'quicklogic', 'part': 'ql-eos-s3'},
{'tool': 'symbiflow_write_fasm', 'image': 'gcr.io/hdl-containers/conda/symbiflow/eos-s3', 'init': symbiflow_init, 'vendor': 'quicklogic', 'part': 'ql-eos-s3'},
{'tool': 'symbiflow_write_bitstream', 'image': 'gcr.io/hdl-containers/conda/symbiflow/eos-s3', 'init': symbiflow_init, 'vendor': 'quicklogic', 'part': 'ql-eos-s3'},
{'tool': 'symbiflow_write_binary', 'image': 'gcr.io/hdl-containers/conda/symbiflow/eos-s3', 'init': symbiflow_init, 'vendor': 'quicklogic', 'part': 'ql-eos-s3'},
{'tool': 'symbiflow_write_bitheader', 'image': 'gcr.io/hdl-containers/conda/symbiflow/eos-s3', 'init': symbiflow_init, 'vendor': 'quicklogic', 'part': 'ql-eos-s3'},
# For Xilinx xc7a35/50t
{'tool': 'symbiflow_synth', 'image': 'gcr.io/hdl-containers/conda/symbiflow/xc7/a50t', 'init': symbiflow_init, 'vendor': 'xilinx', 'part': 'xc7a50t'},
{'tool': 'symbiflow_pack', 'image': 'gcr.io/hdl-containers/conda/symbiflow/xc7/a50t', 'init': symbiflow_init, 'vendor': 'xilinx', 'part': 'xc7a50t'},
{'tool': 'symbiflow_place', 'image': 'gcr.io/hdl-containers/conda/symbiflow/xc7/a50t', 'init': symbiflow_init, 'vendor': 'xilinx', 'part': 'xc7a50t'},
{'tool': 'symbiflow_route', 'image': 'gcr.io/hdl-containers/conda/symbiflow/xc7/a50t', 'init': symbiflow_init, 'vendor': 'xilinx', 'part': 'xc7a50t'},
{'tool': 'symbiflow_write_fasm', 'image': 'gcr.io/hdl-containers/conda/symbiflow/xc7/a50t', 'init': symbiflow_init, 'vendor': 'xilinx', 'part': 'xc7a50t'},
{'tool': 'symbiflow_write_bitstream', 'image': 'gcr.io/hdl-containers/conda/symbiflow/xc7/a50t', 'init': symbiflow_init, 'vendor': 'xilinx', 'part': 'xc7a50t'},
# For Xilinx xc7a100t
{'tool': 'symbiflow_synth', 'image': 'gcr.io/hdl-containers/conda/symbiflow/xc7/a100t', 'init': symbiflow_init, 'vendor': 'xilinx', 'part': 'xc7a100t'},
{'tool': 'symbiflow_pack', 'image': 'gcr.io/hdl-containers/conda/symbiflow/xc7/a100t', 'init': symbiflow_init, 'vendor': 'xilinx', 'part': 'xc7a100t'},
{'tool': 'symbiflow_place', 'image': 'gcr.io/hdl-containers/conda/symbiflow/xc7/a100t', 'init': symbiflow_init, 'vendor': 'xilinx', 'part': 'xc7a100t'},
{'tool': 'symbiflow_route', 'image': 'gcr.io/hdl-containers/conda/symbiflow/xc7/a100t', 'init': symbiflow_init, 'vendor': 'xilinx', 'part': 'xc7a100t'},
{'tool': 'symbiflow_write_fasm', 'image': 'gcr.io/hdl-containers/conda/symbiflow/xc7/a100t', 'init': symbiflow_init, 'vendor': 'xilinx', 'part': 'xc7a100t'},
{'tool': 'symbiflow_write_bitstream', 'image': 'gcr.io/hdl-containers/conda/symbiflow/xc7/a100t', 'init': symbiflow_init, 'vendor': 'xilinx', 'part': 'xc7a100t'},
# For Xilinx xc7a200t
{'tool': 'symbiflow_synth', 'image': 'gcr.io/hdl-containers/conda/symbiflow/xc7/a200t', 'init': symbiflow_init, 'vendor': 'xilinx', 'part': 'xc7a200t'},
{'tool': 'symbiflow_pack', 'image': 'gcr.io/hdl-containers/conda/symbiflow/xc7/a200t', 'init': symbiflow_init, 'vendor': 'xilinx', 'part': 'xc7a200t'},
{'tool': 'symbiflow_place', 'image': 'gcr.io/hdl-containers/conda/symbiflow/xc7/a200t', 'init': symbiflow_init, 'vendor': 'xilinx', 'part': 'xc7a200t'},
{'tool': 'symbiflow_route', 'image': 'gcr.io/hdl-containers/conda/symbiflow/xc7/a200t', 'init': symbiflow_init, 'vendor': 'xilinx', 'part': 'xc7a200t'},
{'tool': 'symbiflow_write_fasm', 'image': 'gcr.io/hdl-containers/conda/symbiflow/xc7/a200t', 'init': symbiflow_init, 'vendor': 'xilinx', 'part': 'xc7a200t'},
{'tool': 'symbiflow_write_bitstream', 'image': 'gcr.io/hdl-containers/conda/symbiflow/xc7/a200t', 'init': symbiflow_init, 'vendor': 'xilinx', 'part': 'xc7a200t'},
# For Xilinx xc7z010
{'tool': 'symbiflow_synth', 'image': 'gcr.io/hdl-containers/conda/symbiflow/xc7/z010', 'init': symbiflow_init, 'vendor': 'xilinx', 'part': 'xc7z010'},
{'tool': 'symbiflow_pack', 'image': 'gcr.io/hdl-containers/conda/symbiflow/xc7/z010', 'init': symbiflow_init, 'vendor': 'xilinx', 'part': 'xc7z010'},
{'tool': 'symbiflow_place', 'image': 'gcr.io/hdl-containers/conda/symbiflow/xc7/z010', 'init': symbiflow_init, 'vendor': 'xilinx', 'part': 'xc7z010'},
{'tool': 'symbiflow_route', 'image': 'gcr.io/hdl-containers/conda/symbiflow/xc7/z010', 'init': symbiflow_init, 'vendor': 'xilinx', 'part': 'xc7z010'},
{'tool': 'symbiflow_write_fasm', 'image': 'gcr.io/hdl-containers/conda/symbiflow/xc7/z010', 'init': symbiflow_init, 'vendor': 'xilinx', 'part': 'xc7z010'},
{'tool': 'symbiflow_write_bitstream', 'image': 'gcr.io/hdl-containers/conda/symbiflow/xc7/z010', 'init': symbiflow_init, 'vendor': 'xilinx', 'part': 'xc7z010'},
# For Xilinx xc7z020
{'tool': 'symbiflow_synth', 'image': 'gcr.io/hdl-containers/conda/symbiflow/xc7/z020', 'init': symbiflow_init, 'vendor': 'xilinx', 'part': 'xc7z020'},
{'tool': 'symbiflow_pack', 'image': 'gcr.io/hdl-containers/conda/symbiflow/xc7/z020', 'init': symbiflow_init, 'vendor': 'xilinx', 'part': 'xc7z020'},
{'tool': 'symbiflow_place', 'image': 'gcr.io/hdl-containers/conda/symbiflow/xc7/z020', 'init': symbiflow_init, 'vendor': 'xilinx', 'part': 'xc7z020'},
{'tool': 'symbiflow_route', 'image': 'gcr.io/hdl-containers/conda/symbiflow/xc7/z020', 'init': symbiflow_init, 'vendor': 'xilinx', 'part': 'xc7z020'},
{'tool': 'symbiflow_write_fasm', 'image': 'gcr.io/hdl-containers/conda/symbiflow/xc7/z020', 'init': symbiflow_init, 'vendor': 'xilinx', 'part': 'xc7z020'},
{'tool': 'symbiflow_write_bitstream', 'image': 'gcr.io/hdl-containers/conda/symbiflow/xc7/z020', 'init': symbiflow_init, 'vendor': 'xilinx', 'part': 'xc7z020'},
]
# fmt: on
tool = sys.argv[1]
toolname = os.path.basename(tool)
# If command exists locally, use it
if shutil.which(toolname) is not None:
cmd = sys.argv[1:]
# If it's in the container list, wrap with Docker
elif [c for c in containers if c["tool"] == toolname]:
(build_root, work) = os.path.split(os.getcwd())
dockerEnv = ""
for k in os.environ:
dockerEnv = dockerEnv + "-e " + k + "=" " + os.environ[k] + " " "
runtool = os.path.relpath(os.path.realpath(tool), os.getcwd())
for c in containers:
# Vendor neutral
if c["tool"] == toolname and c["vendor"] == "":
image = c["image"]
init = c["init"]
break
# Vendor specific toolchain
elif (
c["tool"] == toolname
and c["vendor"] == os.environ.get("EDALIZE_VENDOR")
and os.environ.get("EDALIZE_PART") in c["part"]
):
image = c["image"]
init = c["init"]
break
else:
image = ""
init = ""
if image:
logger.info("Will use image '{}' with init '{}'".format(image, init))
else:
logger.error("Tool {} not found in container list.".format(toolname))
exit(1)
prefix = [
"docker",
"run",
"--rm",
"-v",
build_root + ":/src",
# '-e', dockerEnv,
"-w",
"/src/" + work,
image,
]
if init:
c = init.format("\"" + runtool + " " + " ".join(sys.argv[2:]) + "\"")
else:
c = runtool + " " + shlex.join(sys.argv[2:])
cmd = prefix + shlex.split(c)
# Otherwise, run it locally
else:
cmd = sys.argv[1:]
# print(cmd)
logger.info("Wrapper Command: " + " ".join(cmd))
sys.exit(subprocess.call(cmd, env=os.environ))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment