Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions .github/workflows/build-vivado-image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
name: Build Vivado Docker Image

# Manually triggered. Builds a ~35 GB Vivado 2025.2 ML Standard image (Artix-7 only)
# and pushes to ghcr.io/<owner>/t27-vivado:2025.2.
#
# Prerequisite (one-time): upload Vivado_2025.2_Lin64.bin (347 MB) as a GitHub Release
# asset on a tag like `vivado-installer-2025.2`. Or use a self-hosted runner with the
# installer pre-staged. We default to fetching from the release asset.

on:
workflow_dispatch:
inputs:
installer_release_tag:
description: 'GitHub Release tag holding Vivado_2025.2_Lin64.bin (private asset)'
required: false
default: 'vivado-installer-2025.2'
image_tag:
description: 'Image tag to push (e.g. 2025.2, 2025.2-artix7)'
required: false
default: '2025.2'

permissions:
contents: read
packages: write

jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 240 # full Vivado install can take 60-90 min
steps:
- name: Maximize build space
uses: easimon/maximize-build-space@v10
with:
root-reserve-mb: 4096
swap-size-mb: 1024
remove-dotnet: 'true'
remove-android: 'true'
remove-haskell: 'true'
remove-codeql: 'true'
remove-docker-images: 'true'

- uses: actions/checkout@v4

- name: Download Vivado installer from release asset
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ github.event.inputs.installer_release_tag }}
run: |
gh release download "$TAG" \
--repo "$GITHUB_REPOSITORY" \
--pattern "Vivado_2025.2_Lin64.bin" \
--dir infra/vivado-docker/
ls -lah infra/vivado-docker/Vivado_2025.2_Lin64.bin

- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
driver-opts: image=moby/buildkit:latest

- name: Compute lowercase image name
id: img
run: |
OWNER_LC="$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]')"
echo "name=ghcr.io/${OWNER_LC}/t27-vivado" >> "$GITHUB_OUTPUT"

- name: Build and push image
uses: docker/build-push-action@v6
with:
context: infra/vivado-docker
file: infra/vivado-docker/Dockerfile
push: true
tags: |
${{ steps.img.outputs.name }}:${{ github.event.inputs.image_tag }}
${{ steps.img.outputs.name }}:latest
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Print summary
run: |
echo "Pushed ${{ steps.img.outputs.name }}:${{ github.event.inputs.image_tag }}"
echo "Used by .github/workflows/vivado-synth.yml via container: directive."
37 changes: 0 additions & 37 deletions .github/workflows/vivado-bitstream.yml

This file was deleted.

142 changes: 142 additions & 0 deletions .github/workflows/vivado-synth.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
name: Vivado Synth (Docker, GH-hosted)

# Runs Vivado synth/P&R/bitstream inside a pre-built Docker image on GitHub-hosted runners.
# No self-hosted runner, no Railway. Image is built by build-vivado-image.yml.
#
# Triggers:
# - PR touching fpga/, specs/fpga/, bootstrap/ or this workflow
# - workflow_dispatch with design selection

on:
workflow_dispatch:
inputs:
design:
description: 'Design to build (blinky | gf16 | phi_heartbeat)'
required: false
default: 'gf16'
uart:
description: 'Emit UART telemetry harness (true/false)'
required: false
default: 'true'
type: boolean
# PR trigger intentionally disabled until ghcr.io/<owner>/t27-vivado:2025.2 exists.
# After running build-vivado-image.yml once, re-enable by uncommenting the pull_request block:
# pull_request:
# paths:
# - 'fpga/vivado/**'
# - 'specs/fpga/**'
# - 'bootstrap/src/**'
# - '.github/workflows/vivado-synth.yml'

permissions:
contents: read
packages: read

jobs:
resolve-image:
runs-on: ubuntu-latest
outputs:
image: ${{ steps.img.outputs.image }}
steps:
- id: img
run: |
OWNER_LC="$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]')"
echo "image=ghcr.io/${OWNER_LC}/t27-vivado:2025.2" >> "$GITHUB_OUTPUT"

synth:
needs: resolve-image
runs-on: ubuntu-latest
timeout-minutes: 90
container:
image: ${{ needs.resolve-image.outputs.image }}
credentials:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
options: --user 0
steps:
- name: Maximize build space (host side)
uses: easimon/maximize-build-space@v10
with:
root-reserve-mb: 8192
swap-size-mb: 1024
remove-dotnet: 'true'
remove-android: 'true'
remove-haskell: 'true'
remove-codeql: 'true'

- uses: actions/checkout@v4
with:
fetch-depth: 1

- name: Print runner & Vivado info
run: |
uname -a
which vivado
vivado -version | head -3
df -h /

- name: Install Rust toolchain
run: |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable
echo "${HOME}/.cargo/bin" >> "$GITHUB_PATH"

- name: Build t27c (release)
run: |
cd bootstrap
cargo build --release --bin t27c
echo "T27C_BIN=$PWD/target/release/t27c" >> "$GITHUB_ENV"

- name: Generate Verilog from .t27 specs
run: |
DESIGN="${{ github.event.inputs.design || 'gf16' }}"
"$T27C_BIN" fpga-build --smoke \
--device xc7a100tcsg324-1 \
--top "${DESIGN}_top" \
--output build/fpga
ls -la build/fpga/generated/ || true

- name: Run Vivado synth+P&R+bitstream
env:
DESIGN: ${{ github.event.inputs.design || 'gf16' }}
UART_FLAG: ${{ github.event.inputs.uart || 'true' }}
run: |
cd fpga/vivado
vivado -mode batch -nojournal -nolog \
-source "build_${DESIGN}.tcl" \
-tclargs --uart "$UART_FLAG" --device xc7a100tcsg324-1 \
2>&1 | tee vivado.log

- name: Extract utilization & timing summary (inline awk)
run: |
cd fpga/vivado
awk '
/Slice LUTs/ {print}
/Slice Registers/ {print}
/Block RAM Tile/ {print}
/DSPs/ {print}
/WNS\(ns\)/ {getline; print "WNS line:", $0}
' vivado.log > utilization_summary.txt || true
echo "==== utilization_summary.txt ===="
cat utilization_summary.txt

- name: Upload bitstream
uses: actions/upload-artifact@v4
if: always()
with:
name: vivado-bitstream-${{ github.event.inputs.design || 'gf16' }}
path: |
fpga/vivado/*.bit
fpga/vivado/*.ltx
retention-days: 30
if-no-files-found: warn

- name: Upload Vivado logs & utilization
uses: actions/upload-artifact@v4
if: always()
with:
name: vivado-log-${{ github.event.inputs.design || 'gf16' }}
path: |
fpga/vivado/vivado.log
fpga/vivado/utilization_summary.txt
retention-days: 30
if-no-files-found: warn
2 changes: 1 addition & 1 deletion docs/NOW.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Current Work — Trinity t27

**Last updated:** 2026-05-14
**Note:** GF16 4×4 matmul validated on FPGA @ 323 MHz, 40350 LUTs, 64 DSP48E1, 0 latches. **TinyTapeout TTSKY26a submitted** — `gHashTag/tt-trinity-gf16`, CI running. 41.2 GOPS @ 323 MHz | 12.8 GOPS @ 100 MHz.
**Note:** GF16 4×4 matmul validated on FPGA @ 323 MHz, 40350 LUTs, 64 DSP48E1, 0 latches. **TinyTapeout TTSKY26a submitted** — `gHashTag/tt-trinity-gf16`, CI running. 41.2 GOPS @ 323 MHz | 12.8 GOPS @ 100 MHz. **Vivado CI now runs entirely in GitHub Actions** via a pre-built Docker image on ghcr.io — no self-hosted runner, no Railway. See `infra/vivado-docker/README.md` (PR #622).

---

Expand Down
59 changes: 59 additions & 0 deletions infra/vivado-docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Vivado 2025.2 ML Standard image for t27 FPGA CI.
# Strips down install to Artix-7 family only (~35 GB on disk) to fit
# GitHub-hosted ubuntu-latest runner after easimon/maximize-build-space.
#
# Build context expects: Vivado_2025.2_Lin64.bin and install_config.txt next to this Dockerfile.
# Built once and pushed to ghcr.io/ghashtag/t27-vivado:2025.2 by
# .github/workflows/build-vivado-image.yml (manual dispatch).

FROM ubuntu:22.04 AS installer

ENV DEBIAN_FRONTEND=noninteractive TZ=UTC

# Runtime + installer dependencies for Vivado (libtinfo5 / libncurses5 are the classic gotchas).
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates curl wget xz-utils \
libc6 libstdc++6 libtinfo5 libncurses5 libx11-6 libxext6 libxrender1 libxtst6 libxi6 \
libfontconfig1 libfreetype6 libgtk2.0-0 libcanberra-gtk-module libgomp1 \
libusb-1.0-0 libssl-dev libtinfo-dev \
locales \
&& locale-gen en_US.UTF-8 \
&& rm -rf /var/lib/apt/lists/*

ENV LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8

COPY Vivado_2025.2_Lin64.bin /tmp/installer.bin
COPY install_config.txt /tmp/install_config.txt

RUN chmod +x /tmp/installer.bin \
&& /tmp/installer.bin \
--agree XilinxEULA,3rdPartyEULA \
--batch Install \
--config /tmp/install_config.txt \
&& rm -f /tmp/installer.bin /tmp/install_config.txt \
&& find /opt/Xilinx -name "*.tar.gz" -delete 2>/dev/null || true \
&& find /opt/Xilinx -name "*.zip" -delete 2>/dev/null || true \
&& rm -rf /opt/Xilinx/Vivado/2025.2/data/parts/xilinx/{kintex,virtex,zynq,versal,spartan,ultrascale}* 2>/dev/null || true

# Final stage: copy only the pruned install to keep image lean.
FROM ubuntu:22.04

ENV DEBIAN_FRONTEND=noninteractive TZ=UTC LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8

RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
libc6 libstdc++6 libtinfo5 libncurses5 libx11-6 libxext6 libxrender1 libxtst6 libxi6 \
libfontconfig1 libfreetype6 libgtk2.0-0 libcanberra-gtk-module libgomp1 \
libusb-1.0-0 libssl-dev \
locales tini \
&& locale-gen en_US.UTF-8 \
&& rm -rf /var/lib/apt/lists/*

COPY --from=installer /opt/Xilinx /opt/Xilinx

ENV PATH="/opt/Xilinx/Vivado/2025.2/bin:${PATH}" \
XILINXD_LICENSE_FILE=""

WORKDIR /work
ENTRYPOINT ["/usr/bin/tini", "--"]
CMD ["bash"]
Loading
Loading