Skip to content

Add GPU arrival time readback for timing-aware VCD output #168

Add GPU arrival time readback for timing-aware VCD output

Add GPU arrival time readback for timing-aware VCD output #168

Workflow file for this run

name: CI
on:
push:
branches: [main, staged-aig-release]
pull_request:
branches: [main, staged-aig-release]
permissions:
contents: write
env:
CARGO_TERM_COLOR: always
jobs:
# Run library unit tests on Linux (no GPU required)
test:
name: Unit Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: false
- name: Init required submodules
run: git submodule update --init vendor/eda-infra-rs vendor/sky130_fd_sc_hd
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Run library tests
run: cargo test --lib
# Build and run Metal simulation on macOS
metal:
name: Metal Tests (macOS)
runs-on: macos-latest-xlarge
steps:
- uses: actions/checkout@v4
with:
submodules: false
- name: Init required submodules
run: git submodule update --init vendor/eda-infra-rs vendor/sky130_fd_sc_hd
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Install LLVM (for OpenMP support)
run: |
brew install llvm
LLVM_PREFIX=$(brew --prefix llvm)
echo "CC=${LLVM_PREFIX}/bin/clang" >> $GITHUB_ENV
echo "CXX=${LLVM_PREFIX}/bin/clang++" >> $GITHUB_ENV
# Use Homebrew LLVM's libc++ for linking (not runtime — DYLD_LIBRARY_PATH
# poisons system frameworks). LIBRARY_PATH is used by the compiler for -lc++.
echo "LIBRARY_PATH=${LLVM_PREFIX}/lib/c++:${LLVM_PREFIX}/lib" >> $GITHUB_ENV
LLVM_VER=$(${LLVM_PREFIX}/bin/clang --version | head -1 | grep -o '[0-9]*\.[0-9]*\.[0-9]*')
echo "LLVM_VERSION=$LLVM_VER" >> $GITHUB_ENV
- name: Cache cargo
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-metal-llvm${{ env.LLVM_VERSION }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-metal-llvm${{ env.LLVM_VERSION }}-
- name: Build loom (Metal)
run: cargo build --release --features metal --bin loom
- name: Run Metal simulation (timing test)
run: |
# Capture timing output
time (cargo run --release --features metal --bin loom -- sim \
tests/timing_test/dff_test_synth.gv \
tests/timing_test/dff_test.gemparts \
tests/timing_test/dff_test.vcd \
tests/timing_test/ci_output.vcd \
1) 2>&1 | tee metal_timing.txt
- name: Report Metal performance
run: |
{
echo "## Metal Simulation Performance"
echo "\`\`\`"
cat metal_timing.txt
echo "\`\`\`"
} >> "$GITHUB_STEP_SUMMARY"
- name: Verify simulation output
run: |
echo "Comparing GEM output with golden VCD..."
# Extract q signal values from both VCDs and compare
# Golden VCD uses 'q' signal, GEM output should match
if diff -q <(grep '^[01]!' tests/timing_test/dff_test.vcd | head -20) \
<(grep '^[01]!' tests/timing_test/ci_output.vcd | head -20); then
echo "✓ VCD outputs match!"
else
echo "VCD comparison (first 20 signal changes):"
echo "=== Golden (iverilog) ==="
grep '^[01]!' tests/timing_test/dff_test.vcd | head -20
echo "=== GEM output ==="
grep '^[01]!' tests/timing_test/ci_output.vcd | head -20
echo ""
echo "Note: Minor differences may be acceptable depending on signal timing"
fi
- name: Run Metal simulation with X-propagation
run: |
cargo run --release --features metal --bin loom -- map \
tests/timing_test/dff_test_synth.gv \
tests/timing_test/dff_test_xprop.gemparts \
--xprop
cargo run --release --features metal --bin loom -- sim \
tests/timing_test/dff_test_synth.gv \
tests/timing_test/dff_test_xprop.gemparts \
tests/timing_test/dff_test.vcd \
tests/timing_test/ci_xprop_output.vcd \
1 --xprop 2>&1 | tee metal_xprop.txt
- name: Verify X-propagation output
run: |
# The dff_test has 1 DFF, so Q should start as X then become known
if grep -q 'x' tests/timing_test/ci_xprop_output.vcd; then
echo "X-propagation VCD contains X values as expected"
else
echo "WARNING: No X values in xprop output VCD"
fi
- name: Upload VCD artifacts
uses: actions/upload-artifact@v4
if: always()
with:
name: vcd-outputs
path: |
tests/timing_test/ci_output.vcd
tests/timing_test/ci_xprop_output.vcd
metal_timing.txt
metal_xprop.txt
perf_metrics/
# Build and run CUDA simulation on NVIDIA GPU
cuda:
name: CUDA Tests
runs-on: nvidia-runner-1
steps:
- uses: actions/checkout@v4
with:
submodules: false
- name: Init required submodules
run: git submodule update --init vendor/eda-infra-rs vendor/sky130_fd_sc_hd
- name: Install CUDA Toolkit (driver already present on GPU runner)
run: |
nvidia-smi
sudo apt-get update
sudo apt-get install -y --no-install-recommends cuda-toolkit-12-8
echo "PATH=/usr/local/cuda-12.8/bin:$PATH" >> $GITHUB_ENV
echo "CUDA_PATH=/usr/local/cuda-12.8" >> $GITHUB_ENV
/usr/local/cuda-12.8/bin/nvcc --version
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-cuda-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-cuda-
- name: Build loom (CUDA)
run: cargo build --release --features cuda --bin loom
- name: Run CUDA simulation (timing test)
run: |
time (cargo run --release --features cuda --bin loom -- sim \
tests/timing_test/dff_test_synth.gv \
tests/timing_test/dff_test.gemparts \
tests/timing_test/dff_test.vcd \
tests/timing_test/ci_cuda_output.vcd \
1) 2>&1 | tee cuda_timing.txt
- name: Report CUDA performance
run: |
{
echo "## CUDA Simulation Performance"
echo "\`\`\`"
cat cuda_timing.txt
echo "\`\`\`"
} >> "$GITHUB_STEP_SUMMARY"
- name: Run CUDA simulation with X-propagation
run: |
cargo run --release --features cuda --bin loom -- map \
tests/timing_test/dff_test_synth.gv \
tests/timing_test/dff_test_xprop.gemparts \
--xprop
cargo run --release --features cuda --bin loom -- sim \
tests/timing_test/dff_test_synth.gv \
tests/timing_test/dff_test_xprop.gemparts \
tests/timing_test/dff_test.vcd \
tests/timing_test/ci_cuda_xprop_output.vcd \
1 --xprop 2>&1 | tee cuda_xprop.txt
- name: Verify CUDA X-propagation output
run: |
if grep -q 'x' tests/timing_test/ci_cuda_xprop_output.vcd; then
echo "X-propagation VCD contains X values as expected"
else
echo "WARNING: No X values in CUDA xprop output VCD"
fi
- name: Upload CUDA artifacts
uses: actions/upload-artifact@v4
if: always()
with:
name: cuda-outputs
path: |
tests/timing_test/ci_cuda_output.vcd
tests/timing_test/ci_cuda_xprop_output.vcd
cuda_timing.txt
cuda_xprop.txt
perf_metrics/
# Check formatting and clippy
# Note: Currently set to warn-only due to existing issues in codebase
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: false
- name: Init required submodules
run: git submodule update --init vendor/eda-infra-rs vendor/sky130_fd_sc_hd
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- name: Check formatting
run: cargo fmt --all -- --check || echo "::warning::Formatting issues found"
- name: Clippy
run: cargo clippy --lib 2>&1 | tee clippy_output.txt || true
- name: Check for clippy errors (not warnings)
run: |
if grep -q "^error" clippy_output.txt; then
echo "Clippy errors found!"
exit 1
fi
# Run benchmarks and track performance
benchmark:
name: Benchmarks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: false
- name: Init required submodules
run: git submodule update --init vendor/eda-infra-rs
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-bench-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-bench-
- name: Run benchmarks
run: |
cargo bench --bench event_buffer -- --noplot 2>&1 | tee benchmark_results.txt
cargo bench --bench xprop -- --noplot 2>&1 | tee -a benchmark_results.txt
- name: Extract benchmark summary
run: |
{
echo "## Benchmark Results"
echo "\`\`\`"
grep -E "^(event_buffer|buffer_ops|xprop)" benchmark_results.txt | head -30 || true
grep -E "time:" benchmark_results.txt | head -30 || true
echo "\`\`\`"
} >> "$GITHUB_STEP_SUMMARY"
- name: Upload benchmark results
uses: actions/upload-artifact@v4
with:
name: benchmark-results
path: benchmark_results.txt
# Run GPU simulation on macOS with pre-built post-P&R netlist
mcu-soc-metal:
name: MCU SoC Metal Simulation
runs-on: macos-latest-xlarge
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
persist-credentials: true
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Install LLVM (for OpenMP support)
run: |
brew install llvm
LLVM_PREFIX=$(brew --prefix llvm)
echo "CC=${LLVM_PREFIX}/bin/clang" >> $GITHUB_ENV
echo "CXX=${LLVM_PREFIX}/bin/clang++" >> $GITHUB_ENV
echo "LIBRARY_PATH=${LLVM_PREFIX}/lib/c++:${LLVM_PREFIX}/lib" >> $GITHUB_ENV
LLVM_VER=$(${LLVM_PREFIX}/bin/clang --version | head -1 | grep -o '[0-9]*\.[0-9]*\.[0-9]*')
echo "LLVM_VERSION=$LLVM_VER" >> $GITHUB_ENV
- name: Install uv
uses: astral-sh/setup-uv@v4
- name: Cache cargo
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-mcu-soc-llvm${{ env.LLVM_VERSION }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-mcu-soc-llvm${{ env.LLVM_VERSION }}-
- name: Build firmware
run: |
cd designs/mcu_soc_sky130
uv sync
uv run chipflow software
- name: Generate sim_config.json
run: |
python3 scripts/gen_sim_config.py \
designs/mcu_soc_sky130/pins.lock tests/mcu_soc/ \
--netlist tests/mcu_soc/data/6_final.v \
--firmware designs/mcu_soc_sky130/build/software/software.bin \
--port-mapping
- name: Compile gemparts
timeout-minutes: 10
run: |
cargo run --release --features metal --bin loom -- map \
tests/mcu_soc/data/6_final.v tests/mcu_soc/data/result.gemparts \
--top-module top
- name: Run GPU co-simulation (100K ticks)
timeout-minutes: 10
run: |
cargo run --release --features metal --bin loom -- cosim \
tests/mcu_soc/data/6_final.v tests/mcu_soc/data/result.gemparts \
--config tests/mcu_soc/sim_config.json --top-module top \
--max-cycles 100000 \
2>&1 | tee cosim_output.txt
- name: Verify UART boot output
continue-on-error: true
run: |
if grep -q "nyaa" cosim_output.txt; then
echo "MCU SoC booted successfully - UART output detected"
else
echo "WARNING: Expected UART output 'nyaa' not found (may need more cycles without SDF)"
echo "--- Last 50 lines of simulation output ---"
tail -50 cosim_output.txt
exit 1
fi
- name: Report simulation results
if: always()
run: |
{
echo "## MCU SoC Metal Co-simulation"
echo "\`\`\`"
tail -20 cosim_output.txt
echo "\`\`\`"
} >> "$GITHUB_STEP_SUMMARY"
- name: Upload artifacts
uses: actions/upload-artifact@v4
if: always()
with:
name: mcu-soc-results
path: |
cosim_output.txt
tests/mcu_soc/sim_config.json
# Build documentation (cargo doc + mdbook) and deploy to GitHub Pages
docs:
name: Documentation
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: false
- name: Init required submodules
run: git submodule update --init vendor/eda-infra-rs vendor/sky130_fd_sc_hd
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-docs-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-docs-
- name: Build cargo docs
run: cargo doc --no-deps --lib
- name: Install mdbook
run: |
mkdir -p ~/.cargo/bin
curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.4.44/mdbook-v0.4.44-x86_64-unknown-linux-gnu.tar.gz \
| tar -xz -C ~/.cargo/bin
- name: Build mdbook
run: mdbook build
- name: Prepare Pages site
run: |
cp -r target/doc book/api
touch book/.nojekyll
- name: Deploy to gh-pages branch
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./book
# Run SKY130 post-layout timing simulation
sky130-timing:
name: SKY130 Timing Simulation
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: false
- name: Init required submodules
run: git submodule update --init vendor/eda-infra-rs vendor/sky130_fd_sc_hd
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Install uv
uses: astral-sh/setup-uv@v4
- name: Cache cargo
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-sky130-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-sky130-
- name: Cache SKY130 PDK
uses: actions/cache@v4
with:
path: ~/.volare
key: sky130-pdk-c6d73a35f524070e85faff4a6a9eef49553ebc2b
restore-keys: |
sky130-pdk-
- name: Install volare and download SKY130 PDK
run: |
uv tool install volare
# Use the same PDK version as OpenLane 2.x for reproducibility
volare enable --pdk sky130 c6d73a35f524070e85faff4a6a9eef49553ebc2b
- name: Build timing simulator
run: cargo build --release --bin timing_sim_cpu
- name: Run SKY130 timing simulation
timeout-minutes: 5
run: |
# Build command arguments (using default timing values)
ARGS=(
tests/timing_test/minimal_build/6_final.v
tests/timing_test/6_final_test_input.vcd
--clock-period 25000
--max-cycles 10
)
# Add SDF back-annotation if available
SDF_FILE="tests/timing_test/minimal_build/6_final.sdf"
if [ -f "$SDF_FILE" ]; then
echo "Running timing simulation with SDF back-annotation"
ARGS+=(--sdf "$SDF_FILE" --sdf-corner typ)
else
echo "Running timing simulation with default timing values"
echo "(SDF file not available — add 6_final.sdf for post-layout accuracy)"
fi
# Run timing simulation on the post-P&R netlist
cargo run --release --bin timing_sim_cpu -- "${ARGS[@]}" 2>&1 | tee sky130_timing.txt
- name: Report SKY130 timing results
run: |
{
echo "## SKY130 Timing Simulation Results"
echo "\`\`\`"
cat sky130_timing.txt
echo "\`\`\`"
} >> "$GITHUB_STEP_SUMMARY"
- name: Check timing passed
run: |
if grep -q "TIMING: PASSED" sky130_timing.txt; then
echo "SKY130 timing simulation passed!"
elif grep -q "TIMING: FAILED" sky130_timing.txt; then
echo "SKY130 timing simulation found violations (expected for this test design)"
# Don't fail - timing violations are informational
else
echo "Error: Could not determine timing result"
exit 1
fi
- name: Upload timing results
uses: actions/upload-artifact@v4
if: always()
with:
name: sky130-timing-results
path: sky130_timing.txt
# CVC reference simulation for timing correctness validation
cvc-reference:
name: CVC Reference Simulation
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: false
- name: Init required submodules
run: git submodule update --init vendor/eda-infra-rs vendor/sky130_fd_sc_hd
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Install uv
uses: astral-sh/setup-uv@v4
- name: Cache cargo
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-cvc-ref-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-cvc-ref-
- name: Cache CVC binary
id: cache-cvc
uses: actions/cache@v4
with:
path: ~/cvc/bin
key: cvc-binary-v1
- name: Build CVC from source
if: steps.cache-cvc.outputs.cache-hit != 'true'
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends build-essential zlib1g-dev
git clone --depth 1 https://github.com/cambridgehackers/open-src-cvc.git ~/cvc-src
cd ~/cvc-src/src
make -f makefile.cvc64 -j$(nproc)
mkdir -p ~/cvc/bin
cp cvc64 ~/cvc/bin/
- name: Add CVC to PATH
run: echo "$HOME/cvc/bin" >> "$GITHUB_PATH"
- name: Run CVC simulation with SDF
working-directory: tests/timing_test/inv_chain_pnr
run: |
echo "=== Running CVC on inv_chain with SDF ==="
# +typdelays selects typical corner; $sdf_annotate in tb_cvc.v loads the SDF file
cvc64 +typdelays tb_cvc.v inv_chain.v 2>&1 | tee cvc_compile.log
./cvcsim 2>&1 | tee cvc_output.log
echo ""
echo "=== CVC simulation complete ==="
- name: Validate CVC timing results
working-directory: tests/timing_test/inv_chain_pnr
run: |
echo "Checking CVC produced timing results..."
if grep -q "RESULT: total_delay=" cvc_output.log; then
echo "CVC timing results found:"
grep "RESULT:" cvc_output.log
else
echo "ERROR: CVC did not produce expected RESULT: lines"
echo "--- Full CVC output ---"
cat cvc_output.log
exit 1
fi
- name: Build timing simulator
run: cargo build --release --bin timing_sim_cpu
- name: Run Loom timing_sim_cpu with SDF
run: |
TEST_DIR=tests/timing_test/inv_chain_pnr
echo "=== Running timing_sim_cpu on inv_chain with SDF ==="
cargo run --release --bin timing_sim_cpu -- \
"$TEST_DIR/inv_chain.v" \
"$TEST_DIR/inv_chain_stimulus.vcd" \
--clock-period 10000 \
--max-cycles 8 \
--sdf "$TEST_DIR/inv_chain_test_ps.sdf" \
--sdf-corner typ \
--watchlist "$TEST_DIR/watchlist.json" \
--trace-output "$TEST_DIR/loom_trace.csv" \
--report-violations \
2>&1 | tee "$TEST_DIR/loom_output.log"
echo ""
echo "=== Loom timing_sim_cpu complete ==="
- name: Compare CVC VCD output
working-directory: tests/timing_test/inv_chain_pnr
run: |
echo "=== Comparing simulation outputs ==="
# Compare timing measurements from CVC stdout
# CVC RESULT: lines report IOPATH+INTERCONNECT delays
# Expected: clk_to_q ~350ps, total_delay ~1323ps (IOPATH+wire)
CVC_TOTAL=$(grep "RESULT: total_delay=" cvc_output.log | sed 's/.*=//')
echo "CVC total delay: ${CVC_TOTAL}ps"
# Basic sanity check: total delay should be between 800ps and 2000ps
if [ "$CVC_TOTAL" -gt 800 ] && [ "$CVC_TOTAL" -lt 2000 ]; then
echo "PASS: CVC total delay is in expected range (800-2000ps)"
else
echo "FAIL: CVC total delay ${CVC_TOTAL}ps outside expected range"
exit 1
fi
# Check Loom completed successfully
if grep -q "TIMING: PASSED\|TIMING: FAILED" loom_output.log; then
echo "PASS: Loom timing_sim_cpu completed successfully"
grep "TIMING:" loom_output.log
else
echo "WARNING: Could not determine Loom timing result"
tail -20 loom_output.log
fi
# If CVC produced a VCD, compare it
if [ -f cvc_inv_chain_output.vcd ]; then
echo ""
echo "=== VCD file comparison ==="
echo "CVC VCD size: $(wc -c < cvc_inv_chain_output.vcd) bytes"
echo "Loom trace CSV:"
cat loom_trace.csv || echo "(no trace CSV produced)"
fi
- name: Report results
if: always()
run: |
{
echo "## CVC Reference Simulation Results"
echo ""
echo "### CVC Output"
echo "\`\`\`"
cat tests/timing_test/inv_chain_pnr/cvc_output.log 2>/dev/null || echo "No CVC output"
echo "\`\`\`"
echo ""
echo "### Loom Output"
echo "\`\`\`"
tail -30 tests/timing_test/inv_chain_pnr/loom_output.log 2>/dev/null || echo "No Loom output"
echo "\`\`\`"
echo ""
echo "### Loom Trace"
echo "\`\`\`"
cat tests/timing_test/inv_chain_pnr/loom_trace.csv 2>/dev/null || echo "No trace CSV"
echo "\`\`\`"
} >> "$GITHUB_STEP_SUMMARY"
- name: Upload artifacts
uses: actions/upload-artifact@v4
if: always()
with:
name: cvc-reference-results
path: |
tests/timing_test/inv_chain_pnr/cvc_compile.log
tests/timing_test/inv_chain_pnr/cvc_output.log
tests/timing_test/inv_chain_pnr/cvc_inv_chain_output.vcd
tests/timing_test/inv_chain_pnr/loom_output.log
tests/timing_test/inv_chain_pnr/loom_trace.csv