programming fpga works on my desktop
authorJacob Lifshay <programmerjake@gmail.com>
Tue, 19 Apr 2022 07:34:36 +0000 (00:34 -0700)
committerJacob Lifshay <programmerjake@gmail.com>
Tue, 19 Apr 2022 07:34:36 +0000 (00:34 -0700)
.gitignore [new file with mode: 0644]
.gitlab-ci.yml
blinky.py [new file with mode: 0644]
program-fpga.py [new file with mode: 0755]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..4196ccb
--- /dev/null
@@ -0,0 +1 @@
+.gitlab-runner-ccache
index 80ca97df76fccf7a1ffe295900c3cc8d9602241b..e9e2b413e777b8c8c2886cc2001d4071a5a00572 100644 (file)
 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 (file)
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 (executable)
index 0000000..0334601
--- /dev/null
@@ -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)