From: Jacob Lifshay Date: Tue, 19 Apr 2022 07:34:36 +0000 (-0700) Subject: programming fpga works on my desktop X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=0447eefda9e443842e09b11672d73082a3df83c6;p=utils.git programming fpga works on my desktop --- diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4196ccb --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.gitlab-runner-ccache diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 80ca97d..e9e2b41 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,10 +1,109 @@ image: debian:10 +cache: + when: always + paths: + - ccache + build: stage: build before_script: - apt-get update - - apt-get install -y xc3sprog + - >- + apt-get install -y + autoconf + bison + build-essential + ccache + clang + cmake + cython3 + flex + gawk + git + gperf + libboost-all-dev + libeigen3-dev + libffi-dev + libftdi-dev + libreadline-dev + pkg-config + python3 + python3-dev + python3-jinja2 + python3-numpy + python3-pip + python3-setuptools + python3-wheel + tcl-dev + wget + zlib1g-dev + - export PATH="$HOME/.local/bin:/usr/lib/ccache:$PATH" + - export CCACHE_BASEDIR="$PWD" + - if [[ -z "$CCACHE_DIR" ]]; then export CCACHE_DIR="$PWD/ccache"; fi + - export CCACHE_COMPILERCHECK=content + - ccache --zero-stats || true + - ccache --show-stats || true + - pip3 install textx fasm + - export XC3SPROG="$PWD/program-fpga.py" script: - - ls -l /dev/tty* - - xc3sprog -c nexys4 -T -j + - git clone --depth 1 https://github.com/YosysHQ/yosys.git yosys + - pushd yosys + - git rev-parse HEAD + - make config-gcc + - make -j$(nproc) + - make install + - popd + - yosys -V + + - git clone https://gitlab.com/nmigen/nmigen.git nmigen + - pushd nmigen + - git rev-parse HEAD + - if [[ -z "`git tag --list v0.2`" ]]; then git tag v0.2 1025b98c279a5889e58bc27e7f183928dbbd1735; fi + - python3 setup.py develop + - popd + + - git clone --depth 1 https://gitlab.com/nmigen/nmigen-boards.git nmigen-boards + - pushd nmigen-boards + - git rev-parse HEAD + - python3 setup.py develop + - popd + + - git clone https://github.com/f4pga/prjxray.git prjxray + - pushd prjxray + - git checkout 18b92012afe2b03f3f975a78c4372c74b60dca0c + - git submodule update --init --recursive + - mkdir build; cd build + - cmake -DCMAKE_INSTALL_PREFIX=/usr/local/nextpnr-xilinx .. + - make -j$(nproc) + - make install + - install -d -m 0755 /usr/local/nextpnr-xilinx/build/tools + - install -m 0755 tools/{bitread,bittool,frame_address_decoder,gen_part_base_yaml,segmatch,xc7frames2bit,xc7patch} /usr/local/nextpnr-xilinx/build/tools + - cd .. + - cp -dpr utils /usr/local/nextpnr-xilinx + - sed -i -e '/^# Vivado /,$d' /usr/local/nextpnr-xilinx/utils/environment.sh + - python3 setup.py develop + - popd + + - git clone https://github.com/f4pga/prjxray-db.git prjxray-db + - pushd prjxray-db + - git archive --format=tar --prefix=database/ 0a0addedd73e7e4139d52a6d8db4258763e0f1f3 | tar -C /usr/local/nextpnr-xilinx -xf - + - popd + + - git clone https://github.com/gatecat/nextpnr-xilinx.git nextpnr-xilinx + - pushd nextpnr-xilinx + - git checkout 565588a69ea95a52f7c7592f4ed81d9bef6cfb60 + - git submodule update --init --recursive + - cmake -DARCH=xilinx -DBUILD_GUI=OFF -DCMAKE_INSTALL_PREFIX=/usr/local/nextpnr-xilinx . + - make -j$(nproc) + - make install + - ln -s xc7a100tcsg324-1 xilinx/external/prjxray-db/artix7/xc7a100t + - python3 xilinx/python/bbaexport.py --device xc7a100tcsg324-1 --bba xilinx/xc7a100t.bba + - ./bbasm --l xilinx/xc7a100t.bba xilinx/xc7a100t.bin + - install -d -m 0755 /usr/local/nextpnr-xilinx/share/xilinx + - install -m 0755 xilinx/xc7a100t.bin /usr/local/nextpnr-xilinx/share/xilinx + - popd + - export PATH=/usr/local/nextpnr-xilinx/bin:$PATH + - export XRAY_DIR=/usr/local/nextpnr-xilinx + + - python3 blinky.py diff --git a/blinky.py b/blinky.py new file mode 100644 index 0000000..ac30d4d --- /dev/null +++ b/blinky.py @@ -0,0 +1,5 @@ +from nmigen_boards.arty_a7 import ArtyA7_100Platform +from nmigen_boards.test.blinky import Blinky + +platform = ArtyA7_100Platform(toolchain="yosys_nextpnr") +platform.build(Blinky(), do_program=True) diff --git a/program-fpga.py b/program-fpga.py new file mode 100755 index 0000000..0334601 --- /dev/null +++ b/program-fpga.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 +import io +import pty +import socket +import socketserver +import os +import sys + +SOCKET_PATH = os.environ.get('ARTY_PROG_SOCKET', '/arty-prog-socket') +LAST_BIT_FILE = "last.bit" +CHUNK_SIZE_FIELD_SIZE = 4 +MAX_CHUNK_SIZE = 0x10 ** CHUNK_SIZE_FIELD_SIZE + + +class InvalidSize(IOError): + pass + + +def recv_chunk(sock: socket.socket): + size = sock.recv(CHUNK_SIZE_FIELD_SIZE) + # print("recv size:", size, flush=True) + if size == b'' or size == b'Z' * CHUNK_SIZE_FIELD_SIZE: + return b'' + if len(size) != CHUNK_SIZE_FIELD_SIZE or not size.isalnum(): + raise InvalidSize("invalid size", size) + try: + size = int(b'0x' + size, 16) + except ValueError: + raise InvalidSize("invalid size", size) + assert size >= 0 + if size == 0: + size = MAX_CHUNK_SIZE + if size > MAX_CHUNK_SIZE: + raise InvalidSize("invalid size", size) + retval = b'' + while len(retval) < size: + buf = sock.recv(size - len(retval)) + # print("recv buf:", len(buf), flush=True) + if buf == b'': + raise EOFError() + retval += buf + return retval + + +def send_chunk(sock: socket.socket, chunk: bytes): + size = len(chunk) + assert size <= MAX_CHUNK_SIZE + if size > 0: + if size == MAX_CHUNK_SIZE: + size = 0 + size = hex(size)[2:].encode().zfill(CHUNK_SIZE_FIELD_SIZE) + else: + size = b'Z' * CHUNK_SIZE_FIELD_SIZE + # print("send size:", size, flush=True) + sock.sendall(size) + # print("send buf:", len(chunk), flush=True) + sock.sendall(chunk) + + +class RequestHandler(socketserver.BaseRequestHandler): + request: socket.socket + + def handle(self): + with open(LAST_BIT_FILE, 'wb') as file: + while True: + buf = recv_chunk(self.request) + if len(buf) == 0: + break + file.write(buf) + + def read_output(fd): + buf = os.read(fd, 8192) + send_chunk(self.request, buf) + return buf + cmd_line = ['xc3sprog', '-c', 'nexys4', LAST_BIT_FILE] + send_chunk(self.request, f"{' '.join(cmd_line)}\n".encode()) + status = pty.spawn(cmd_line, read_output) + send_chunk(self.request, b'') + send_chunk(self.request, str(status).encode()) + + +if sys.argv[1:] == ["--daemon"]: + try: + os.unlink(SOCKET_PATH) + except FileNotFoundError: + pass + fd = os.open("/dev/null", os.O_RDONLY) + os.dup2(fd, sys.stdin.fileno()) + os.close(fd) + with socketserver.UnixStreamServer(SOCKET_PATH, RequestHandler) as server: + server.allow_reuse_address = True + server.serve_forever() +else: + assert len(sys.argv) == 4, "invalid command line" + assert sys.argv[1] == '-c', "invalid command line" + assert sys.argv[2] == 'nexys4', "invalid command line" + file_name = sys.argv[3] + assert not file_name.startswith('-'), "invalid command line" + assert ':' not in file_name, "programming options not supported" + assert file_name != '', "invalid command line" + with open(file_name, 'rb') as file: + with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as client: + client.connect(SOCKET_PATH) + while True: + buf = file.read(MAX_CHUNK_SIZE) + if len(buf) == 0: + break + send_chunk(client, buf) + send_chunk(client, b'') + with os.fdopen(sys.stdout.fileno(), "wb", closefd=False, + buffering=0) as stdout: + while True: + buf = recv_chunk(client) + if len(buf) == 0: + break + stdout.write(buf) + status = recv_chunk(client) + if status != b'0': + print(f"Failed: status: {status}", file=sys.stderr) + exit(1)